Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement cloud #301

Merged
merged 9 commits into from
Feb 24, 2023
7 changes: 5 additions & 2 deletions src/lib/layout/unauthenticated.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import { base } from '$app/paths';
import { user } from '$lib/stores/user';

export let imgLight = LoginLight;
export let imgDark = LoginDark;

const technologies = [
'js',
'flutter',
Expand Down Expand Up @@ -38,9 +41,9 @@
class="container u-margin-block-start-20 is-not-mobile"
style="--p-container-max-size: var(--container-size-large);">
{#if $app.themeInUse === 'dark'}
<img src={LoginDark} alt="" class="u-only-dark" />
<img src={imgDark} alt="" class="u-only-dark" />
{:else}
<img src={LoginLight} alt="" class="u-only-light" />
<img src={imgLight} alt="" class="u-only-light" />
{/if}
</div>

Expand Down
31 changes: 31 additions & 0 deletions src/lib/sdk/project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Models, Payload } from '@aw-labs/appwrite-console';
import { Service } from './service';

export class Project extends Service {
/**
* Get usage stats for a project
*
*
* @param {string} range
* @throws {AppwriteException}
* @returns {Promise}
*/
async getUsage(range?: string): Promise<Models.UsageProject> {
const path = '/project/usage';
const payload: Payload = {};

if (typeof range !== 'undefined') {
payload['range'] = range;
}

const uri = new URL(this.client.config.endpoint + path);
return await this.client.call(
'get',
uri,
{
'content-type': 'application/json'
},
payload
);
}
}
28 changes: 28 additions & 0 deletions src/lib/sdk/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Client, Payload } from '@aw-labs/appwrite-console';

export class Service {
static CHUNK_SIZE = 5 * 1024 * 1024; // 5MB

client: Client;

constructor(client: Client) {
this.client = client;
}

static flatten(data: Payload, prefix = ''): Payload {
let output: Payload = {};

for (const key in data) {
const value = data[key];
const finalKey = prefix ? `${prefix}[${key}]` : key;

if (Array.isArray(value)) {
output = Object.assign(output, this.flatten(value, finalKey));
} else {
output[finalKey] = value;
}
}

return output;
}
}
2 changes: 2 additions & 0 deletions src/lib/stores/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Project } from '$lib/sdk/project';
import { VARS } from '$lib/system';
import {
Account,
Expand Down Expand Up @@ -42,6 +43,7 @@ const sdkForProject = {
functions: new Functions(clientProject),
health: new Health(clientProject),
locale: new Locale(clientProject),
project: new Project(clientProject),
projects: new Projects(clientProject),
storage: new Storage(clientProject),
teams: new Teams(clientProject),
Expand Down
2 changes: 2 additions & 0 deletions src/lib/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ export const ENV = {
};

export const MODE = VARS.CONSOLE_MODE === Mode.CLOUD ? Mode.CLOUD : Mode.SELF_HOSTED;
export const isCloud = MODE === Mode.CLOUD;
export const isSelfHosted = MODE === Mode.SELF_HOSTED;
12 changes: 12 additions & 0 deletions src/routes/+error.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script>
import { page } from '$app/stores';
import { Heading } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { Unauthenticated } from '$lib/layout';
</script>

<Unauthenticated>
<Heading size="1" tag="h3">{$page.status}</Heading>
<Heading size="3" tag="h4">{$page.error.message}</Heading>
<Button href="/">Back to the console</Button>
</Unauthenticated>
2 changes: 1 addition & 1 deletion src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
}
loading.set(false);
} else {
if (acceptedRoutes.includes($page.url.pathname)) {
if (acceptedRoutes.some((n) => $page.url.pathname.startsWith(n))) {
await goto(`${base}${$page.url.pathname}${$page.url.search}`);
} else {
await goto(`${base}/login`, {
Expand Down
2 changes: 1 addition & 1 deletion src/routes/+layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const load: LayoutLoad = async ({ depends, url }) => {
'/auth/oauth2/failure'
];

if (!acceptedRoutes.includes(url.pathname)) {
if (!acceptedRoutes.some((n) => url.pathname.startsWith(n))) {
throw redirect(303, '/login');
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { Container } from '$lib/layout';
import { Card, SecondaryTabsItem, SecondaryTabs, Heading } from '$lib/components';
import { last } from '$lib/layout/usage.svelte';
import { total } from '$lib/layout/usage.svelte';
import { BarChart } from '$lib/charts';
import { page } from '$app/stores';
import type { PageData } from './$types';
Expand Down Expand Up @@ -35,7 +35,7 @@
</div>
{#if count}
<Card>
<Heading tag="h6" size="6">{last(count).value}</Heading>
<Heading tag="h6" size="6">{total(count)}</Heading>
<p>Executions</p>
<div class="u-margin-block-start-16" />
<BarChart
Expand All @@ -49,7 +49,7 @@
{/if}
{#if errors}
<Card>
<Heading tag="h6" size="6">{last(errors).value}</Heading>
<Heading tag="h6" size="6">{total(errors)}</Heading>
<p>Errors</p>
<div class="u-margin-block-start-16" />
<BarChart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export const load: PageLoad = async ({ params, parent }) => {

return {
count: response.executionsTotal as unknown as Models.Metric[],
errors: response.buildsFailure as unknown as Models.Metric[]
errors: response.executionsFailure as unknown as Models.Metric[]
};
};
12 changes: 6 additions & 6 deletions src/routes/console/project-[project]/overview/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<Onboard {projectId} />
{:else}
{#if $usage}
{@const storage = humanFileSize(last($usage.storage)?.value ?? 0)}
{@const storage = humanFileSize(total($usage.storage) ?? 0)}
<section class="common-section">
<div class="grid-dashboard-1s-2m-6l">
<div class="card is-2-columns-medium-screen is-3-columns-large-screen">
Expand All @@ -91,14 +91,14 @@

<div class="grid-item-1-end-start">
<div class="heading-level-4">
{format(last($usage.documents)?.value ?? 0)}
{format(total($usage.documents) ?? 0)}
</div>
<div>Documents</div>
</div>

<div class="grid-item-1-end-end">
<div class="text">
Databases: {format(last($usage.databases)?.value ?? 0)}
Databases: {format(total($usage.databases) ?? 0)}
</div>
</div>
</div>
Expand Down Expand Up @@ -126,7 +126,7 @@

<div class="grid-item-1-end-end">
<div class="text">
Buckets: {format(last($usage.buckets)?.value ?? 0)}
Buckets: {format(total($usage.buckets) ?? 0)}
</div>
</div>
</div>
Expand All @@ -146,7 +146,7 @@

<div class="grid-item-1-end-start">
<div class="heading-level-4">
{format(last($usage.users)?.value ?? 0)}
{format(total($usage.users) ?? 0)}
</div>
<div>Users</div>
</div>
Expand All @@ -167,7 +167,7 @@

<div class="grid-item-1-end-start">
<div class="heading-level-4">
{format(last($usage.executions)?.value ?? 0)}
{format(total($usage.executions) ?? 0)}
</div>
<div>Executions</div>
</div>
Expand Down
7 changes: 5 additions & 2 deletions src/routes/console/project-[project]/overview/store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { sdkForConsole } from '$lib/stores/sdk';
import { sdkForConsole, sdkForProject } from '$lib/stores/sdk';
import { cachedStore } from '$lib/helpers/cache';
import type { Models } from '@aw-labs/appwrite-console';
import { writable, type Writable } from 'svelte/store';
import { isCloud } from '$lib/system';

export const usage = cachedStore<
Models.UsageProject,
Expand All @@ -11,7 +12,9 @@ export const usage = cachedStore<
>('projectUsage', function ({ set }) {
return {
load: async (projectId, range) => {
const usages = await sdkForConsole.projects.getUsage(projectId, range);
const usages = isCloud
? await sdkForProject.project.getUsage(range)
: await sdkForConsole.projects.getUsage(projectId, range);
set(usages);
}
};
Expand Down
9 changes: 9 additions & 0 deletions src/routes/register/invite/+layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { isSelfHosted } from '$lib/system';
import { error } from '@sveltejs/kit';
import type { LayoutLoad } from './$types';

export const load: LayoutLoad = async () => {
if (isSelfHosted) {
throw error(501, 'Not available in self hosted.');
}
};
147 changes: 147 additions & 0 deletions src/routes/register/invite/[slug]/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<script lang="ts">
import { goto, invalidate } from '$app/navigation';
import { base } from '$app/paths';
import {
Button,
Form,
FormItem,
InputChoice,
InputEmail,
InputPassword,
InputText
} from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import { Unauthenticated } from '$lib/layout';
import FormList from '$lib/elements/forms/formList.svelte';
import { Dependencies } from '$lib/constants';
import { trackEvent } from '$lib/actions/analytics';
import { onMount } from 'svelte';
import { page } from '$app/stores';
import LoginLight from '$lib/images/login/login-light-mode.svg';
import LoginDark from '$lib/images/login/login-dark-mode.svg';
import { isCloud, VARS } from '$lib/system';

let slug = $page.params.slug;
let imgLight = LoginLight;
let imgDark = LoginDark;

onMount(async () => {
if (isCloud) {
switch (slug) {
case 'mlh':
imgDark = (await import('./mlh-dark.svg')).default;
imgLight = (await import('./mlh-light.svg')).default;
title = 'Welcome MLH Hackers!';
break;
case 'appwrite':
imgDark = (await import('$lib/images/appwrite.svg')).default;
imgLight = (await import('$lib/images/appwrite.svg')).default;
title = 'Welcome Appwriters!';

break;
}
}
});

let name: string, mail: string, pass: string, code: string, disabled: boolean;
let title = 'Sign up';
let terms = false;

async function invite() {
try {
disabled = true;
const res = await fetch(`${VARS.APPWRITE_ENDPOINT}/account/invite`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId: 'unique()',
email: mail,
password: pass,
code: code,
name: name ?? ''
})
});
if (!res.ok) {
throw new Error((await res.json()).message);
} else {
await sdkForConsole.account.createEmailSession(mail, pass);
await sdkForConsole.account.updatePrefs({ code });
await invalidate(Dependencies.ACCOUNT);
await goto(`${base}/console`);
trackEvent('submit_account_create', { code: code });
}
} catch (error) {
disabled = false;
addNotification({
type: 'error',
message: error.message
});
}
}
</script>

<svelte:head>
<title>Sign up - Appwrite</title>
</svelte:head>

<Unauthenticated {imgLight} {imgDark}>
<svelte:fragment slot="title">{title}</svelte:fragment>
<svelte:fragment>
<Form on:submit={invite}>
<FormList>
<InputText
id="name"
label="Name"
placeholder="Your name"
autofocus={true}
bind:value={name} />
<InputEmail
id="email"
label="Email"
placeholder="Your email"
required={true}
bind:value={mail} />
<InputPassword
id="password"
label="Password"
placeholder="Your password"
required={true}
showPasswordButton={true}
bind:value={pass} />
<InputText
id="Code"
label="Code"
placeholder="Your code"
required={true}
bind:value={code} />
<InputChoice required value={terms} id="terms" label="terms" showLabel={false}>
By registering, you agree that you have read, understand, and acknowledge our <a
class="link"
href="https://appwrite.io/policy/privacy"
target="_blank"
rel="noopener noreferrer">
Privacy Policy</a>
and accept our
<a
class="link"
href="https://appwrite.io/policy/terms"
target="_blank"
rel="noopener noreferrer">General Terms of Use</a
>.</InputChoice>
<FormItem>
<Button fullWidth submit {disabled}>Sign up</Button>
</FormItem>
</FormList>
</Form>
</svelte:fragment>
<svelte:fragment slot="links">
<li class="inline-links-item">
<span class="text">
Already got an account? <a class="link" href={`${base}/login`}>Sign in</a>
</span>
</li>
</svelte:fragment>
</Unauthenticated>
Loading