mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'feat-pink-v2' of https://github.com/appwrite/console into feat-pink-v2-legacy-fixes
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "1.0.0-next.7",
|
||||
"@appwrite.io/pink-legacy": "^1.0.0",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@3114ccc",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@4a73ad4",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
|
||||
Generated
+33
-10
@@ -21,8 +21,8 @@ importers:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@3114ccc
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@3114ccc(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@4a73ad4
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@4a73ad4(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
|
||||
'@popperjs/core':
|
||||
specifier: ^2.11.8
|
||||
version: 2.11.8
|
||||
@@ -223,8 +223,8 @@ packages:
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@3114ccc0b95c9d94385c06730b7aeaef7b4a81fc':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@3114ccc0b95c9d94385c06730b7aeaef7b4a81fc}
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@4a73ad48c64ce1078565d893a9c91d887d05d90a':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@4a73ad48c64ce1078565d893a9c91d887d05d90a}
|
||||
version: 1.0.0-next.7
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -238,8 +238,8 @@ packages:
|
||||
'@appwrite.io/pink-legacy@1.0.0':
|
||||
resolution: {integrity: sha512-EZk/wX8oDhIsCXf2A0oQjdNOaHvh5MnWbLQI4pAiGrdy5qQGPoU19AK6iVX6z3QoOq7geoEu0zVwAowBy5hZbQ==}
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@3114ccc':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@3114ccc}
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@4a73ad4':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@4a73ad4}
|
||||
version: 1.0.0-next.85
|
||||
peerDependencies:
|
||||
react-dom: ^18.0.0
|
||||
@@ -694,6 +694,11 @@ packages:
|
||||
peerDependencies:
|
||||
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.118
|
||||
|
||||
'@melt-ui/svelte@0.86.2':
|
||||
resolution: {integrity: sha512-wRVN603oIt1aXvx2QRmKqVDJgTScSvr/WJLLokkD8c4QzHgn6pfpPtUKmhV6Dvkk+OY89OG/1Irkd6ouA50Ztw==}
|
||||
peerDependencies:
|
||||
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.118
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -4087,7 +4092,7 @@ snapshots:
|
||||
dependencies:
|
||||
svelte: 4.2.19
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@3114ccc0b95c9d94385c06730b7aeaef7b4a81fc(svelte@4.2.19)':
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@4a73ad48c64ce1078565d893a9c91d887d05d90a(svelte@4.2.19)':
|
||||
dependencies:
|
||||
svelte: 4.2.19
|
||||
|
||||
@@ -4100,11 +4105,12 @@ snapshots:
|
||||
'@appwrite.io/pink-icons': 1.0.0
|
||||
the-new-css-reset: 1.11.3
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@3114ccc(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@4a73ad4(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@3114ccc0b95c9d94385c06730b7aeaef7b4a81fc(svelte@4.2.19)
|
||||
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@4a73ad48c64ce1078565d893a9c91d887d05d90a(svelte@4.2.19)
|
||||
'@floating-ui/dom': 1.6.13
|
||||
'@melt-ui/svelte': 0.83.0(svelte@4.2.19)
|
||||
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.2(svelte@4.2.19))(svelte@4.2.19)
|
||||
'@melt-ui/svelte': 0.86.2(svelte@4.2.19)
|
||||
pretty-bytes: 6.1.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
shiki: 1.27.2
|
||||
@@ -4618,6 +4624,13 @@ snapshots:
|
||||
magic-string: 0.30.17
|
||||
svelte: 4.2.19
|
||||
|
||||
'@melt-ui/pp@0.3.2(@melt-ui/svelte@0.86.2(svelte@4.2.19))(svelte@4.2.19)':
|
||||
dependencies:
|
||||
'@melt-ui/svelte': 0.86.2(svelte@4.2.19)
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.17
|
||||
svelte: 4.2.19
|
||||
|
||||
'@melt-ui/svelte@0.83.0(svelte@4.2.19)':
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.6.9
|
||||
@@ -4628,6 +4641,16 @@ snapshots:
|
||||
nanoid: 5.0.9
|
||||
svelte: 4.2.19
|
||||
|
||||
'@melt-ui/svelte@0.86.2(svelte@4.2.19)':
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.6.9
|
||||
'@floating-ui/dom': 1.6.13
|
||||
'@internationalized/date': 3.6.0
|
||||
dequal: 2.0.3
|
||||
focus-trap: 7.6.4
|
||||
nanoid: 5.0.9
|
||||
svelte: 4.2.19
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
//temporarily unefined
|
||||
export let title: Props['title'] = undefined;
|
||||
export let disabled = false;
|
||||
export let src: string = null;
|
||||
export let alt: string = null;
|
||||
|
||||
// TODO: remove after label card migration
|
||||
let slotTitle: HTMLSpanElement;
|
||||
@@ -26,6 +28,8 @@
|
||||
<Tooltip disabled={!tooltipText || !tooltipShow} inline={false}>
|
||||
<div>
|
||||
<Card.Selector
|
||||
{src}
|
||||
{alt}
|
||||
{padding}
|
||||
{imageRadius}
|
||||
{variant}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
import type { ComponentType } from 'svelte';
|
||||
|
||||
export let id: string;
|
||||
export let label: string | undefined = undefined;
|
||||
@@ -11,6 +12,7 @@
|
||||
value: string | boolean | number | null;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
leadingIcon?: ComponentType;
|
||||
}[];
|
||||
|
||||
let element: HTMLSelectElement;
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
import ignore from 'ignore';
|
||||
|
||||
export interface FileData {
|
||||
path: string;
|
||||
buffer: ArrayBuffer;
|
||||
}
|
||||
|
||||
export async function processFileList(files: FileList): Promise<FileData[]> {
|
||||
const fileArray = Array.from(files);
|
||||
const ignorer = ignore();
|
||||
|
||||
// Check for .gitignore file
|
||||
const gitignoreFile = fileArray.find(
|
||||
(file) => file.webkitRelativePath.endsWith('.gitignore') || file.name === '.gitignore'
|
||||
);
|
||||
|
||||
if (gitignoreFile) {
|
||||
const gitignoreContent = await gitignoreFile.text();
|
||||
ignorer.add(gitignoreContent);
|
||||
} else {
|
||||
ignorer.add(defaultIgnore);
|
||||
}
|
||||
|
||||
// Filter files using ignorer
|
||||
const filteredFiles = fileArray.filter((file) => {
|
||||
const path = file.webkitRelativePath || file.name;
|
||||
return !ignorer.ignores(path);
|
||||
});
|
||||
const filePromises = filteredFiles.map(async (file) => {
|
||||
try {
|
||||
const buffer = await file.arrayBuffer();
|
||||
return {
|
||||
path: file?.webkitRelativePath || file.name,
|
||||
buffer: buffer
|
||||
};
|
||||
} catch (e) {
|
||||
// console.log(file);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(filePromises);
|
||||
}
|
||||
|
||||
export const defaultIgnore = `
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
`;
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Trim } from '$lib/components';
|
||||
import { Link, Pill } from '$lib/elements';
|
||||
import { Link } from '$lib/elements';
|
||||
import { protocol } from '$routes/(console)/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { IconExternalLink } from '@appwrite.io/pink-icons-svelte';
|
||||
import { ActionMenu, Icon, Layout, Popover } from '@appwrite.io/pink-svelte';
|
||||
import { ActionMenu, Icon, Layout, Popover, Tag } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let domains: Models.ProxyRuleList;
|
||||
</script>
|
||||
@@ -20,9 +20,9 @@
|
||||
</Link>
|
||||
{#if domains.rules.length > 1}
|
||||
<Popover let:toggle>
|
||||
<Pill button on:click={toggle}>
|
||||
<Tag size="s" on:click={toggle}>
|
||||
+{domains.rules.length - 1}
|
||||
</Pill>
|
||||
</Tag>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
{#each domains.rules as rule, i}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { humanFileSize } from '$lib/helpers/sizeConvertion';
|
||||
import { calculateTime } from '$lib/helpers/timeConversion';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Icon, Image, Layout, Status, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { Divider, Icon, Image, Layout, Status, Typography } from '@appwrite.io/pink-svelte';
|
||||
import DeploymentSource from './deploymentSource.svelte';
|
||||
import DeploymentCreatedBy from './deploymentCreatedBy.svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
@@ -24,92 +24,106 @@
|
||||
</script>
|
||||
|
||||
<Card padding="xs">
|
||||
<Layout.Stack gap="m" direction="row">
|
||||
<Image
|
||||
width={445}
|
||||
height={280}
|
||||
src={`https://placehold.co/600x400/111/bbb?text=Screenshot+coming+soon&font=inter`}
|
||||
alt="Screenshot" />
|
||||
<Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="flex-start">
|
||||
<Layout.Stack direction="row" gap="xl">
|
||||
{#if deployment.status === 'failed'}
|
||||
<Layout.Stack gap="xs" inline>
|
||||
<Typography.Text
|
||||
variant="m-400"
|
||||
color="--color-fgcolor-neutral-tertiary">
|
||||
Status
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
<Status status={deployment.status} label={deployment.status} />
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<Layout.Stack gap="xs" inline>
|
||||
<Typography.Text
|
||||
variant="m-400"
|
||||
color="--color-fgcolor-neutral-tertiary">
|
||||
Deployed
|
||||
</Typography.Text>
|
||||
<DeploymentCreatedBy {deployment} />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack>
|
||||
<Layout.Stack gap="m" direction="row">
|
||||
<Image
|
||||
width={445}
|
||||
height={280}
|
||||
src={`https://placehold.co/600x400/111/bbb?text=Screenshot+coming+soon&font=inter`}
|
||||
alt="Screenshot" />
|
||||
<Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="flex-start">
|
||||
<Layout.Stack direction="row" gap="xl">
|
||||
{#if deployment.status === 'failed'}
|
||||
<Layout.Stack gap="xs" inline>
|
||||
<Typography.Text
|
||||
variant="m-400"
|
||||
color="--color-fgcolor-neutral-tertiary">
|
||||
Status
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
<Status status={deployment.status} label={deployment.status} />
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<Layout.Stack gap="xs" inline>
|
||||
<Typography.Text
|
||||
variant="m-400"
|
||||
color="--color-fgcolor-neutral-tertiary">
|
||||
Deployed
|
||||
</Typography.Text>
|
||||
<DeploymentCreatedBy {deployment} />
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{#if siteUrl}
|
||||
<Button icon text on:click={() => (show = true)}>
|
||||
<Icon icon={IconQrcode} />
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{#if siteUrl}
|
||||
<Button icon text on:click={() => (show = true)}>
|
||||
<Icon icon={IconQrcode} />
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{#if proxyRuleList?.total}
|
||||
<Layout.Stack gap="xs">
|
||||
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
|
||||
Domains
|
||||
</Typography.Text>
|
||||
<DeploymentDomains domains={proxyRuleList} />
|
||||
</Layout.Stack>
|
||||
{:else if deployment.domain}
|
||||
<Layout.Stack gap="xs">
|
||||
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
|
||||
Domains
|
||||
</Typography.Text>
|
||||
<Link external href={`${$protocol}${deployment.domain}`} variant="muted">
|
||||
<Layout.Stack gap="xxs" direction="row" alignItems="center">
|
||||
<Trim alternativeTrim>
|
||||
{deployment.domain}
|
||||
</Trim>
|
||||
<Icon icon={IconExternalLink} size="s" />
|
||||
</Layout.Stack>
|
||||
</Link>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
<Layout.Stack gap="xl" direction="row">
|
||||
{#if deployment?.buildTime}
|
||||
<Layout.Stack gap="xs" inline>
|
||||
{#if proxyRuleList?.total}
|
||||
<Layout.Stack gap="xs">
|
||||
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
|
||||
Build time
|
||||
Domains
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
{calculateTime(deployment.buildTime)}
|
||||
<DeploymentDomains domains={proxyRuleList} />
|
||||
</Layout.Stack>
|
||||
{:else if deployment.domain}
|
||||
<Layout.Stack gap="xs">
|
||||
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
|
||||
Domains
|
||||
</Typography.Text>
|
||||
<Link external href={`${$protocol}${deployment.domain}`} variant="muted">
|
||||
<Layout.Stack gap="xxs" direction="row" alignItems="center">
|
||||
<Trim alternativeTrim>
|
||||
{deployment.domain}
|
||||
</Trim>
|
||||
<Icon icon={IconExternalLink} size="s" />
|
||||
</Layout.Stack>
|
||||
</Link>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
<Layout.Stack gap="xs" inline>
|
||||
<Layout.Stack gap="xl" direction="row">
|
||||
{#if deployment?.buildTime}
|
||||
<Layout.Stack gap="xs" inline>
|
||||
<Typography.Text
|
||||
variant="m-400"
|
||||
color="--color-fgcolor-neutral-tertiary">
|
||||
Build time
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
{calculateTime(deployment.buildTime)}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
<Layout.Stack gap="xs" inline>
|
||||
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
|
||||
Total size
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
{totalSize.value}{totalSize.unit}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="xs">
|
||||
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
|
||||
Total size
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
{totalSize.value}{totalSize.unit}
|
||||
Source
|
||||
</Typography.Text>
|
||||
<DeploymentSource {deployment} />
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="xs">
|
||||
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
|
||||
Source
|
||||
</Typography.Text>
|
||||
<DeploymentSource {deployment} />
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
|
||||
{#if $$slots.footer}
|
||||
<span
|
||||
style="margin-left: calc(-1* var(--space-6));margin-right: calc(-1* var(--space-6));width:auto;">
|
||||
<Divider />
|
||||
</span>
|
||||
<Layout.Stack direction="row-reverse">
|
||||
<slot name="footer" />
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Card } from '$lib/components';
|
||||
import { Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconGithub,
|
||||
IconReact,
|
||||
IconSvelte,
|
||||
IconNuxt,
|
||||
IconVue,
|
||||
IconGitBranch
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { IconGithub, IconGitBranch } from '@appwrite.io/pink-icons-svelte';
|
||||
import { consoleVariables } from '$routes/(console)/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { getFrameworkIcon } from './store';
|
||||
|
||||
export let framework: Partial<Models.Framework>;
|
||||
export let repositoryName: string;
|
||||
@@ -18,21 +12,6 @@
|
||||
export let rootDir: string;
|
||||
export let domain: string = '';
|
||||
export let showAfter = true;
|
||||
|
||||
function getIcon(fr: string) {
|
||||
switch (true) {
|
||||
case fr.toLocaleLowerCase().includes('sveltekit'):
|
||||
return IconSvelte;
|
||||
case fr.toLocaleLowerCase().includes('nuxt'):
|
||||
return IconNuxt;
|
||||
case fr.toLocaleLowerCase().includes('vue'):
|
||||
return IconVue;
|
||||
case fr.toLocaleLowerCase().includes('react'):
|
||||
return IconReact;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card padding="xs">
|
||||
@@ -40,7 +19,7 @@
|
||||
<slot />
|
||||
<Layout.Stack gap="l">
|
||||
{#if framework?.name}
|
||||
{@const frameworkIcon = getIcon(framework.key)}
|
||||
{@const frameworkIcon = getFrameworkIcon(framework.key)}
|
||||
<Layout.Stack gap="xxxs">
|
||||
<Typography.Caption variant="400">Framework</Typography.Caption>
|
||||
<Layout.Stack gap="xs" alignItems="center" direction="row">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { CustomId } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { InputSelect, InputText } from '$lib/elements/forms';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Fieldset, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Fieldset, Layout, Tag } from '@appwrite.io/pink-svelte';
|
||||
import type { ComponentType } from 'svelte';
|
||||
|
||||
export let name: string;
|
||||
export let id: string;
|
||||
@@ -12,6 +12,7 @@
|
||||
export let options: {
|
||||
value: string;
|
||||
label: string;
|
||||
leadingIcon?: ComponentType;
|
||||
}[] = [];
|
||||
|
||||
let showCustomId = false;
|
||||
@@ -31,10 +32,10 @@
|
||||
<CustomId bind:id bind:show={showCustomId} name="Site" fullWidth />
|
||||
{:else}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<Tag size="s" on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" />
|
||||
<span class="text">Site ID </span>
|
||||
</Pill>
|
||||
</Tag>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Layout, Icon, Upload, Typography, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import Details from '../details.svelte';
|
||||
import Configuration from './configuration.svelte';
|
||||
import Aside from './aside.svelte';
|
||||
import { BuildRuntime, Framework, ID, Query } from '@appwrite.io/console';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { processFileList } from '$lib/helpers/files';
|
||||
import { createTarGzip } from 'nanotar';
|
||||
|
||||
export let data;
|
||||
let showExitModal = false;
|
||||
|
||||
let formComponent: Form;
|
||||
let isSubmitting = writable(false);
|
||||
|
||||
let name = '';
|
||||
let id = '';
|
||||
let framework: Models.Framework = data.frameworks.frameworks[0];
|
||||
let adapter = framework?.adapters[0];
|
||||
let installCommand = adapter?.installCommand;
|
||||
let buildCommand = adapter?.buildCommand;
|
||||
let outputDirectory = adapter?.outputDirectory;
|
||||
let variables: Partial<Models.Variable>[] = [];
|
||||
let files: FileList;
|
||||
let uploadFile: File;
|
||||
|
||||
async function handleChange() {
|
||||
const tick = performance.now();
|
||||
if (!files?.length) return;
|
||||
|
||||
// If the file is a tar.gz file, then return it as is
|
||||
if (
|
||||
files?.length === 1 &&
|
||||
files.item(0).type === 'application/gzip' &&
|
||||
files.item(0).name.split('.').pop() === 'tar'
|
||||
) {
|
||||
uploadFile = files.item(0);
|
||||
}
|
||||
// Else process the file to mantain the folder structure and create a .tar.gz file
|
||||
else {
|
||||
try {
|
||||
const processedFiles = await processFileList(files);
|
||||
const tar = await createTarGzip(
|
||||
processedFiles.map((f) => ({
|
||||
name: f.path,
|
||||
data: f.buffer
|
||||
}))
|
||||
);
|
||||
const blob = new Blob([tar], { type: 'application/gzip' });
|
||||
const file = new File([blob], 'deployment.tar.gz', { type: 'application/gzip' });
|
||||
uploadFile = file;
|
||||
} catch (e) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: e
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(uploadFile);
|
||||
const tock = performance.now();
|
||||
console.log('Time taken to process files:', tock - tick);
|
||||
}
|
||||
|
||||
async function create() {
|
||||
try {
|
||||
const fr = Object.values(Framework).find((f) => f === framework.key);
|
||||
const buildRuntime = Object.values(BuildRuntime).find(
|
||||
(f) => f === framework.buildRuntime
|
||||
);
|
||||
let site = await sdk.forProject.sites.create(
|
||||
id || ID.unique(),
|
||||
name,
|
||||
fr,
|
||||
buildRuntime,
|
||||
undefined,
|
||||
undefined,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
outputDirectory,
|
||||
undefined,
|
||||
framework.adapters[Object.keys(framework.adapters)[0]].key, //TODO: fix this
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
|
||||
trackEvent(Submit.SiteCreate, {});
|
||||
|
||||
const { deployments } = await sdk.forProject.sites.listDeployments(site.$id, [
|
||||
Query.limit(1)
|
||||
]);
|
||||
console.log(deployments);
|
||||
const deployment = deployments[0];
|
||||
await goto(
|
||||
`${base}/project-${$page.params.project}/sites/create-site/deploying?site=${site.$id}&deployment=${deployment.$id}`
|
||||
);
|
||||
} catch (e) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: e.message
|
||||
});
|
||||
trackError(e, Submit.SiteCreate);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Create site - Appwrite</title>
|
||||
</svelte:head>
|
||||
|
||||
<Wizard
|
||||
title="Create site"
|
||||
bind:showExitModal
|
||||
href={`${base}/project-${$page.params.project}/sites/`}
|
||||
confirmExit>
|
||||
<Form bind:this={formComponent} onSubmit={create} bind:isSubmitting>
|
||||
<Layout.Stack gap="xl">
|
||||
<Upload.Dropzone folder bind:files on:change={handleChange}>
|
||||
<Layout.Stack alignItems="center" gap="s">
|
||||
<Layout.Stack
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
direction="row"
|
||||
gap="s">
|
||||
<Typography.Text variant="l-500">
|
||||
Drag and drop files here or click to upload
|
||||
</Typography.Text>
|
||||
<Tooltip>
|
||||
<Icon icon={IconInfo} size="s" />
|
||||
<svelte:fragment slot="tooltip">
|
||||
Only PNG, JPEG, PDF files allowed
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
<Typography.Caption variant="400">Max file size: 10MB</Typography.Caption>
|
||||
</Layout.Stack>
|
||||
</Upload.Dropzone>
|
||||
<Details bind:name bind:id />
|
||||
|
||||
<Configuration
|
||||
bind:installCommand
|
||||
bind:buildCommand
|
||||
bind:outputDirectory
|
||||
bind:selectedFramework={framework}
|
||||
bind:variables
|
||||
frameworks={data.frameworks.frameworks} />
|
||||
|
||||
<!-- <Domain /> -->
|
||||
</Layout.Stack>
|
||||
</Form>
|
||||
<svelte:fragment slot="aside">
|
||||
<Aside />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button size="s" fullWidthMobile secondary on:click={() => (showExitModal = true)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
on:click={() => formComponent.triggerSubmit()}
|
||||
disabled={$isSubmitting}>
|
||||
Deploy
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Wizard>
|
||||
@@ -0,0 +1,7 @@
|
||||
export const load = async ({ parent }) => {
|
||||
const { frameworks } = await parent();
|
||||
|
||||
return {
|
||||
frameworks
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Card } from '$lib/components';
|
||||
</script>
|
||||
|
||||
<Card padding="xs">
|
||||
<slot />
|
||||
</Card>
|
||||
@@ -0,0 +1,225 @@
|
||||
<script lang="ts">
|
||||
import { Empty } from '$lib/components';
|
||||
import { Button, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import {
|
||||
Fieldset,
|
||||
Layout,
|
||||
Popover,
|
||||
Icon,
|
||||
Table,
|
||||
Badge,
|
||||
HiddenText,
|
||||
ActionMenu,
|
||||
Accordion
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconDotsHorizontal,
|
||||
IconCode,
|
||||
IconUpload,
|
||||
IconPlus,
|
||||
IconTrash,
|
||||
IconEyeOff,
|
||||
IconPencil
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import SecretVariableModal from '../secretVariableModal.svelte';
|
||||
import ImportSiteVariablesModal from '../importSiteVariablesModal.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { getFrameworkIcon } from '../store';
|
||||
|
||||
export let frameworks: Models.Framework[];
|
||||
export let selectedFramework: Models.Framework;
|
||||
|
||||
export let variables: Partial<Models.Variable>[] = [];
|
||||
export let installCommand = '';
|
||||
export let buildCommand = '';
|
||||
export let outputDirectory = '';
|
||||
|
||||
let showEditorModal = false;
|
||||
let showImportModal = false;
|
||||
let showSecretModal = false;
|
||||
|
||||
let currentVariable: Partial<Models.Variable>;
|
||||
let frameworkId = selectedFramework.key;
|
||||
|
||||
function markAsSecret() {
|
||||
let variable = variables.find((v) => v.key === currentVariable.key);
|
||||
if (variable) {
|
||||
variable.secret = true;
|
||||
}
|
||||
}
|
||||
|
||||
$: frameworkData = frameworks.find((framework) => framework.key === selectedFramework.key);
|
||||
</script>
|
||||
|
||||
<Fieldset legend="Configuration">
|
||||
<Layout.Stack gap="l">
|
||||
<InputSelect
|
||||
id="framework"
|
||||
label="Framework"
|
||||
placeholder="Select framework"
|
||||
bind:value={frameworkId}
|
||||
options={frameworks.map((framework) => ({
|
||||
value: framework.key,
|
||||
label: framework.name
|
||||
}))}
|
||||
on:change={() => {
|
||||
selectedFramework = frameworks.find((framework) => framework.key === frameworkId);
|
||||
}} />
|
||||
|
||||
<Layout.Stack>
|
||||
<Accordion title="Build settings" badge="Optional">
|
||||
<Layout.Stack>
|
||||
Set up how your project is built and where the output files are stored.
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
<InputText
|
||||
id="installCommand"
|
||||
label="Install command"
|
||||
bind:value={installCommand}
|
||||
placeholder={frameworkData?.defaultInstallCommand} />
|
||||
<Button secondary size="s" on:click={() => (installCommand = '')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
<InputText
|
||||
id="buildCommand"
|
||||
label="Build command"
|
||||
bind:value={buildCommand}
|
||||
placeholder={frameworkData?.defaultBuildCommand} />
|
||||
<Button secondary size="s" on:click={() => (buildCommand = '')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
<InputText
|
||||
id="outputDirectory"
|
||||
label="Output directory"
|
||||
bind:value={outputDirectory}
|
||||
placeholder={frameworkData?.defaultOutputDirectory} />
|
||||
<Button secondary size="s" on:click={() => (outputDirectory = '')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Environment variables" badge="Optional" hideDivider>
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack gap="xl">
|
||||
Set up environment variables to securely manage keys and settings for your
|
||||
project.
|
||||
|
||||
{#if variables?.length}
|
||||
<Table.Root>
|
||||
<svelte:fragment slot="header">
|
||||
<Table.Header.Cell width="200px">Key</Table.Header.Cell>
|
||||
<Table.Header.Cell>Value</Table.Header.Cell>
|
||||
<Table.Header.Cell width="10px"></Table.Header.Cell>
|
||||
</svelte:fragment>
|
||||
{#each variables as variable}
|
||||
<Table.Row>
|
||||
<Table.Cell>{variable.key}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<!-- TODO: fix max width -->
|
||||
<div style="max-width: 20rem">
|
||||
{#if variable.secret}
|
||||
<Badge content="Secret" variant="secondary" />
|
||||
{:else}
|
||||
<HiddenText
|
||||
isVisible={false}
|
||||
text={variable.value} />
|
||||
{/if}
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<div style="margin-inline-start: auto">
|
||||
<Popover placement="bottom-end" let:toggle>
|
||||
<Button
|
||||
text
|
||||
icon
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
toggle(e);
|
||||
}}>
|
||||
<Icon size="s" icon={IconDotsHorizontal} />
|
||||
</Button>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconPencil}
|
||||
on:click={() => {
|
||||
showEditorModal = true;
|
||||
}}>
|
||||
Edit
|
||||
</ActionMenu.Item.Button>
|
||||
{#if !variable?.secret}
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconEyeOff}
|
||||
on:click={() => {
|
||||
currentVariable = variable;
|
||||
showSecretModal = true;
|
||||
}}>
|
||||
Secret
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconTrash}
|
||||
on:click={() => {
|
||||
showImportModal = true;
|
||||
}}>
|
||||
Delete
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
{:else}
|
||||
<Empty>Create variables to get started</Empty>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack direction="row">
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => (showEditorModal = true)}>
|
||||
<Icon icon={IconCode} /> Editor
|
||||
</Button>
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => (showImportModal = true)}>
|
||||
<Icon icon={IconUpload} /> Import .env
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
<Button secondary size="s" on:mousedown={() => (showImportModal = true)}>
|
||||
<Icon icon={IconPlus} /> Create variable
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Fieldset>
|
||||
|
||||
{#if showEditorModal}
|
||||
<!-- <RawVariableEditor
|
||||
{isGlobal}
|
||||
{sdkCreateVariable}
|
||||
{sdkUpdateVariable}
|
||||
{sdkDeleteVariable}
|
||||
{variableList}
|
||||
bind:showEditor={showEditorModal} /> -->
|
||||
{/if}
|
||||
|
||||
{#if showSecretModal}
|
||||
<SecretVariableModal bind:show={showSecretModal} on:click={markAsSecret} />
|
||||
{/if}
|
||||
|
||||
{#if showImportModal}
|
||||
<ImportSiteVariablesModal bind:show={showImportModal} bind:variables />
|
||||
{/if}
|
||||
+15
-16
@@ -1,16 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { Collapsible, CollapsibleItem, Empty } from '$lib/components';
|
||||
import { Empty } from '$lib/components';
|
||||
import { Button, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import {
|
||||
Fieldset,
|
||||
Layout,
|
||||
Tag,
|
||||
Popover,
|
||||
Icon,
|
||||
Table,
|
||||
Badge,
|
||||
HiddenText,
|
||||
ActionMenu
|
||||
ActionMenu,
|
||||
Accordion
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconDotsHorizontal,
|
||||
@@ -21,9 +21,10 @@
|
||||
IconEyeOff,
|
||||
IconPencil
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import SecretVariableModal from './secretVariableModal.svelte';
|
||||
import ImportSiteVariablesModal from './importSiteVariablesModal.svelte';
|
||||
import SecretVariableModal from '../../secretVariableModal.svelte';
|
||||
import ImportSiteVariablesModal from '../../importSiteVariablesModal.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { getFrameworkIcon } from '../../store';
|
||||
|
||||
export let frameworks: Models.Framework[];
|
||||
export let selectedFramework: Models.Framework;
|
||||
@@ -59,17 +60,15 @@
|
||||
bind:value={frameworkId}
|
||||
options={frameworks.map((framework) => ({
|
||||
value: framework.key,
|
||||
label: framework.name
|
||||
label: framework.name,
|
||||
leadingIcon: getFrameworkIcon(framework.key)
|
||||
}))}
|
||||
on:change={() => {
|
||||
selectedFramework = frameworks.find((framework) => framework.key === frameworkId);
|
||||
}} />
|
||||
|
||||
<Collapsible>
|
||||
<CollapsibleItem>
|
||||
<svelte:fragment slot="title">
|
||||
Build settings <Tag size="s">Optional</Tag>
|
||||
</svelte:fragment>
|
||||
<Layout.Stack>
|
||||
<Accordion title="Build settings" badge="Optional">
|
||||
<Layout.Stack>
|
||||
Set up how your project is built and where the output files are stored.
|
||||
<Layout.Stack gap="s" direction="row" alignItems="flex-end">
|
||||
@@ -103,11 +102,11 @@
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</CollapsibleItem>
|
||||
</Accordion>
|
||||
|
||||
<CollapsibleItem>
|
||||
<Accordion title="Environment variables" badge="Optional" hideDivider>
|
||||
<svelte:fragment slot="title">
|
||||
Environment variables <Tag size="s">Optional</Tag>
|
||||
Environment variables <Badge content="Optional" variant="secondary" />
|
||||
</svelte:fragment>
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack gap="xl">
|
||||
@@ -206,8 +205,8 @@
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</CollapsibleItem>
|
||||
</Collapsible>
|
||||
</Accordion>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Fieldset>
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
export let show = false;
|
||||
</script>
|
||||
|
||||
<Modal title="Secret variable" size="big" bind:show>
|
||||
<Modal title="Secret variable" bind:show>
|
||||
<Layout.Stack gap="l">
|
||||
<p>
|
||||
Secret variables are hidden from both the UI and API. Once a variable is marked as
|
||||
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
IconAngular,
|
||||
IconNuxt,
|
||||
IconReact,
|
||||
IconSvelte,
|
||||
IconVue
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export function getFrameworkIcon(framework: string) {
|
||||
console.log(framework);
|
||||
switch (true) {
|
||||
case framework.toLocaleLowerCase().includes('sveltekit'):
|
||||
return IconSvelte;
|
||||
case framework.toLocaleLowerCase().includes('nuxt'):
|
||||
return IconNuxt;
|
||||
case framework.toLocaleLowerCase().includes('vue'):
|
||||
return IconVue;
|
||||
case framework.toLocaleLowerCase().includes('react'):
|
||||
return IconReact;
|
||||
case framework.toLocaleLowerCase().includes('angular'):
|
||||
return IconAngular;
|
||||
case framework.toLocaleLowerCase().includes('svelte'):
|
||||
return IconSvelte;
|
||||
case framework.toLocaleLowerCase().includes('next'):
|
||||
return IconReact; //TODO: add next icon
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { Collapsible, CollapsibleItem, EmptySearch, SvgIcon } from '$lib/components';
|
||||
import { EmptySearch } from '$lib/components';
|
||||
import { Button, InputSearch } from '$lib/elements/forms';
|
||||
import { page } from '$app/stores';
|
||||
import Wizard from '$lib/layout/wizard.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { debounce } from '$lib/helpers/debounce.js';
|
||||
import { Card, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { templatesList } from '$lib/stores/templates.js';
|
||||
import { Card, Layout, Accordion, Selector } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let data;
|
||||
|
||||
function applyFilter(filter: string, value: string, event: Event) {
|
||||
const add = (event.target as EventTarget & HTMLInputElement).checked;
|
||||
function applyFilter(filter: string, value: string, event: CustomEvent) {
|
||||
const target = new URL($page.url);
|
||||
if (add) {
|
||||
target.searchParams.append(filter, value);
|
||||
if (event?.detail) {
|
||||
if (
|
||||
!target.searchParams
|
||||
.getAll(filter)
|
||||
.some((n) => n.toLowerCase() === value.toLowerCase())
|
||||
) {
|
||||
target.searchParams.append(filter, value);
|
||||
}
|
||||
} else {
|
||||
const previous = target.searchParams
|
||||
.getAll(filter)
|
||||
@@ -42,27 +46,6 @@
|
||||
}, 250)();
|
||||
}
|
||||
|
||||
function getIconFromFramework(framework: string) {
|
||||
switch (true) {
|
||||
case framework.includes('node'):
|
||||
return 'node';
|
||||
case framework.includes('php'):
|
||||
return 'php';
|
||||
case framework.includes('ruby'):
|
||||
return 'ruby';
|
||||
case framework.includes('python'):
|
||||
return 'python';
|
||||
case framework.includes('dart'):
|
||||
return 'dart';
|
||||
case framework.includes('bun'):
|
||||
return 'bun';
|
||||
case framework.includes('go'):
|
||||
return 'go';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
const target = new URL($page.url);
|
||||
target.search = '';
|
||||
@@ -74,19 +57,6 @@
|
||||
.getAll('useCase')
|
||||
.some((param) => param.toLowerCase() === useCase.toLowerCase());
|
||||
};
|
||||
|
||||
$: getErrorMessage = () => {
|
||||
const searchParams = $page.url.searchParams;
|
||||
const paramsArray = Array.from(searchParams.entries());
|
||||
|
||||
if (paramsArray.length === 1) {
|
||||
const [_, value] = paramsArray[0];
|
||||
return `Sorry, we couldn't find "${value}".`;
|
||||
} else if (paramsArray.length > 1) {
|
||||
return `Sorry, we couldn't find any results with the applied filters.`;
|
||||
}
|
||||
};
|
||||
$: console.log(templatesList);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -101,52 +71,41 @@
|
||||
value={$page.url.searchParams.get('search')}
|
||||
on:clear={clearSearch}
|
||||
on:change={applySearch} />
|
||||
<Collapsible>
|
||||
<CollapsibleItem>
|
||||
<svelte:fragment slot="title">Use case</svelte:fragment>
|
||||
<ul class="form-list u-row-gap-16">
|
||||
|
||||
<Layout.Stack>
|
||||
<Accordion title="Use case">
|
||||
<Layout.Stack>
|
||||
{#each [...data.useCases] as useCase}
|
||||
<li class="form-item">
|
||||
<label class="u-flex u-cross-center u-gap-16">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="is-small"
|
||||
checked={isChecked(useCase)}
|
||||
on:change={(e) => applyFilter('useCase', useCase, e)} />
|
||||
<span class="u-trim-1">{useCase}</span>
|
||||
</label>
|
||||
</li>
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Selector.Checkbox
|
||||
id={useCase}
|
||||
size="s"
|
||||
label={useCase}
|
||||
checked={isChecked(useCase)}
|
||||
on:change={(e) => {
|
||||
applyFilter('useCase', useCase, e);
|
||||
}} />
|
||||
</Layout.Stack>
|
||||
{/each}
|
||||
</ul>
|
||||
</CollapsibleItem>
|
||||
<CollapsibleItem>
|
||||
<svelte:fragment slot="title">Framework</svelte:fragment>
|
||||
<ul class="form-list u-row-gap-16">
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
<Accordion title="Framework">
|
||||
<Layout.Stack>
|
||||
{#each [...data.frameworks] as framework}
|
||||
{@const icon = getIconFromFramework(framework)}
|
||||
<li class="form-item">
|
||||
<label class="u-flex u-cross-center u-gap-16">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="is-small"
|
||||
checked={$page.url.searchParams
|
||||
.getAll('framework')
|
||||
.includes(framework)}
|
||||
on:change={(e) => applyFilter('framework', framework, e)} />
|
||||
<div class="u-flex u-cross-center u-gap-8">
|
||||
<div class="avatar is-size-x-small">
|
||||
<SvgIcon name={icon} iconSize="small" />
|
||||
</div>
|
||||
<div class="u-trim-1 u-capitalize">
|
||||
{framework?.split('-')?.join(' ')}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</li>
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Selector.Checkbox
|
||||
id={framework}
|
||||
size="s"
|
||||
label={framework?.split('-')?.join(' ')}
|
||||
checked={$page.url.searchParams
|
||||
.getAll('framework')
|
||||
.includes(framework)}
|
||||
on:change={(e) => applyFilter('framework', framework, e)} />
|
||||
</Layout.Stack>
|
||||
{/each}
|
||||
</ul>
|
||||
</CollapsibleItem>
|
||||
</Collapsible>
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -170,16 +129,11 @@
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<EmptySearch hidePagination>
|
||||
<div class="common-section">
|
||||
<div class="u-text-center common-section">
|
||||
<b class="body-text-2 u-bold">{getErrorMessage()}</b>
|
||||
<p>There are no templates that match your search.</p>
|
||||
</div>
|
||||
<div class="u-flex u-gap-16 common-section u-main-center">
|
||||
<Button secondary on:click={clearSearch}>Clear search</Button>
|
||||
</div>
|
||||
</div>
|
||||
<EmptySearch
|
||||
hidePagination
|
||||
target="templates"
|
||||
search={$page.url.searchParams.get('search')}>
|
||||
<Button secondary on:click={clearSearch}>Clear search</Button>
|
||||
</EmptySearch>
|
||||
{/if}
|
||||
<!-- <div class="u-flex u-margin-block-start-32 u-main-space-between u-cross-center">
|
||||
|
||||
@@ -20,19 +20,19 @@ export const load = async ({ url, route }) => {
|
||||
const [frameworks, useCases] = siteTemplatesList.templates.reduce(
|
||||
([fr, uc], next) => {
|
||||
next.useCases.forEach((useCase) => uc.add(useCase));
|
||||
// next.frameworks.forEach((framework) => fr.add(framework.name));
|
||||
next.frameworks.forEach((framework) => fr.add(framework.name));
|
||||
return [fr, uc];
|
||||
},
|
||||
[new Set<string>(), new Set<string>()]
|
||||
);
|
||||
|
||||
const templates = siteTemplatesList.templates.filter((template) => {
|
||||
// if (
|
||||
// filter.framework.length > 0
|
||||
// && !template.frameworks.some((n) => filter.frameworks.includes(n.name))
|
||||
// ) {
|
||||
// return false;
|
||||
// }
|
||||
if (
|
||||
filter.frameworks.length > 0 &&
|
||||
!template.frameworks.some((n) => filter.frameworks.includes(n.name))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const filterLowerCases = filter.useCases.map((n) => n.toLowerCase());
|
||||
if (
|
||||
|
||||
+3
-1
@@ -29,6 +29,7 @@
|
||||
import { BuildRuntime, Framework, ID, Query } from '@appwrite.io/console';
|
||||
import Domain from '../../domain.svelte';
|
||||
import { NewRepository, Repositories, RepositoryBehaviour } from '$lib/components/git';
|
||||
import { getFrameworkIcon } from '../../store';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -223,7 +224,8 @@
|
||||
{@const options = data.template.frameworks.map((framework) => {
|
||||
return {
|
||||
value: framework.name,
|
||||
label: framework.name
|
||||
label: framework.name,
|
||||
leadingIcon: getFrameworkIcon(framework.key)
|
||||
};
|
||||
})}
|
||||
<Layout.Stack gap="xxl">
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
|
||||
export const load = async ({ parent, params }) => {
|
||||
const { installations, frameworks } = await parent();
|
||||
let { installations, frameworks } = await parent();
|
||||
const template = await sdk.forProject.sites.getTemplate(params.template);
|
||||
|
||||
return {
|
||||
|
||||
+7
-12
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { Collapsible, CollapsibleItem } from '$lib/components';
|
||||
import {
|
||||
Button,
|
||||
InputEmail,
|
||||
@@ -10,7 +9,7 @@
|
||||
InputURL
|
||||
} from '$lib/elements/forms';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Fieldset, Layout, Tag, Popover, Icon } from '@appwrite.io/pink-svelte';
|
||||
import { Fieldset, Layout, Popover, Icon, Accordion } from '@appwrite.io/pink-svelte';
|
||||
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
|
||||
import type { SvelteComponent } from 'svelte';
|
||||
|
||||
@@ -51,10 +50,9 @@
|
||||
|
||||
<Fieldset legend="Configuration">
|
||||
<Layout.Stack gap="l">
|
||||
<Collapsible>
|
||||
<Layout.Stack>
|
||||
{#if requiredVariables?.length}
|
||||
<CollapsibleItem open>
|
||||
<svelte:fragment slot="title">Required environment variables</svelte:fragment>
|
||||
<Accordion title="Required environment variables" open>
|
||||
<Layout.Stack>
|
||||
Provide the values for the required environment variables to run this
|
||||
application.
|
||||
@@ -94,13 +92,10 @@
|
||||
</Layout.Stack>
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
</CollapsibleItem>
|
||||
</Accordion>
|
||||
{/if}
|
||||
{#if optionalVariables?.length}
|
||||
<CollapsibleItem>
|
||||
<svelte:fragment slot="title">
|
||||
Environment variables <Tag size="s">Optional</Tag>
|
||||
</svelte:fragment>
|
||||
<Accordion title="Environment variables" badge="Optional">
|
||||
<Layout.Stack>
|
||||
Set up environment variables to securely manage keys and settings for your
|
||||
project.
|
||||
@@ -140,8 +135,8 @@
|
||||
</Layout.Stack>
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
</CollapsibleItem>
|
||||
</Accordion>
|
||||
{/if}
|
||||
</Collapsible>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Fieldset>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { LabelCard, Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Card, Modal } from '$lib/components';
|
||||
import { Image, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import RepoDark from './(images)/repositoryDark.svg';
|
||||
@@ -9,57 +8,51 @@
|
||||
import TemplateDark from './(images)/templateDark.svg';
|
||||
import TemplateLight from './(images)/templateLight.svg';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { Link } from '$lib/elements';
|
||||
|
||||
export let show = false;
|
||||
let behaviour: 'repositories' | 'templates';
|
||||
|
||||
$: if (!show) {
|
||||
behaviour = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal title="Create site" bind:show>
|
||||
<Modal title="Create site" bind:show hideFooter>
|
||||
<Layout.Stack direction="row" gap="m" wrap="wrap">
|
||||
<div style="flex-grow: 1; min-width: 245px">
|
||||
<Layout.Stack direction="column" justifyContent="center">
|
||||
<LabelCard
|
||||
variant="secondary"
|
||||
padding="xs"
|
||||
radius="s"
|
||||
imageRadius="xxs"
|
||||
src={$app?.themeInUse === 'dark' ? RepoDark : RepoLight}
|
||||
title="Connect a repository"
|
||||
name="connect"
|
||||
bind:group={behaviour}
|
||||
value="repositories">
|
||||
Connect to one of your existing Git repositories
|
||||
</LabelCard>
|
||||
</Layout.Stack>
|
||||
<Card
|
||||
variant="secondary"
|
||||
padding="xs"
|
||||
radius="s"
|
||||
href={`${base}/project-${$page.params.project}/sites/create-site/templates`}>
|
||||
<Image
|
||||
alt="repositories"
|
||||
radius="xxs"
|
||||
src={$app?.themeInUse === 'dark' ? TemplateDark : TemplateLight} />
|
||||
<Layout.Stack gap="s">
|
||||
<Typography.Text variant="m-600">Clone a template</Typography.Text>
|
||||
Clone a pre-built template to quickly set up your site
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
</div>
|
||||
<div style="flex-grow: 1; min-width: 245px">
|
||||
<Layout.Stack direction="column" justifyContent="center">
|
||||
<LabelCard
|
||||
variant="secondary"
|
||||
padding="xs"
|
||||
radius="s"
|
||||
imageRadius="xxs"
|
||||
src={$app?.themeInUse === 'dark' ? TemplateDark : TemplateLight}
|
||||
title="Clone a template"
|
||||
name="clone"
|
||||
bind:group={behaviour}
|
||||
value="templates">
|
||||
Clone a pre-built template to quickly set up your site
|
||||
</LabelCard>
|
||||
</Layout.Stack>
|
||||
<Card
|
||||
variant="secondary"
|
||||
padding="xs"
|
||||
radius="s"
|
||||
href={`${base}/project-${$page.params.project}/sites/create-site/repositories`}>
|
||||
<Image
|
||||
alt="repositories"
|
||||
radius="xxs"
|
||||
src={$app?.themeInUse === 'dark' ? RepoDark : RepoLight} />
|
||||
|
||||
<Layout.Stack gap="s">
|
||||
<Typography.Text variant="m-600">Connect a repository</Typography.Text>
|
||||
Connect to one of your existing Git repositories
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text size="s" on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button
|
||||
disabled={!behaviour}
|
||||
size="s"
|
||||
href={`${base}/project-${$page.params.project}/sites/create-site/${behaviour}`}>
|
||||
Next
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<Typography.Text variant="m-400">
|
||||
Or simply drag and drop your files to create a site <Link
|
||||
href={`${base}/project-${$page.params.project}/sites/create-site/manual`}>
|
||||
manually</Link
|
||||
>.
|
||||
</Typography.Text>
|
||||
</Modal>
|
||||
|
||||
@@ -4,24 +4,47 @@
|
||||
import SiteCard from '../(components)/siteCard.svelte';
|
||||
import DomainsOverview from './domainsOverview.svelte';
|
||||
import DeploymentsOverview from './deploymentsOverview.svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { protocol } from '$routes/(console)/store';
|
||||
import InstantRollbackDomain from './instantRollbackDomain.svelte';
|
||||
|
||||
export let data;
|
||||
let showRollback = false;
|
||||
|
||||
$: console.log(data.site);
|
||||
$: console.log(data.deployment);
|
||||
$: console.log(data.deploymentList);
|
||||
$: console.log(data.proxyRuleList);
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Layout.Stack gap="xxl">
|
||||
<SiteCard deployment={data.deployment} proxyRuleList={data.proxyRuleList} />
|
||||
<SiteCard deployment={data.deployment} proxyRuleList={data.proxyRuleList}>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button external href={`${$protocol}${data.deployment.domain}`}>Visit</Button>
|
||||
<!-- TODO: disable when disabled={data.hasProdReadyDeployments} -->
|
||||
<Button secondary on:click={() => (showRollback = true)}>Instant rollback</Button>
|
||||
</svelte:fragment>
|
||||
</SiteCard>
|
||||
|
||||
<Divider />
|
||||
<Layout.Stack direction="row" gap="xxl">
|
||||
<DomainsOverview proxyRuleList={data.proxyRuleList} />
|
||||
<DeploymentsOverview
|
||||
activeDeployment={data.deployment}
|
||||
deploymentList={data.deploymentList} />
|
||||
|
||||
<Layout.Stack direction="row" gap="xxl" wrap="wrap">
|
||||
<div style:flex="1">
|
||||
<DomainsOverview proxyRuleList={data.proxyRuleList} />
|
||||
</div>
|
||||
<div style:flex="2">
|
||||
<DeploymentsOverview
|
||||
activeDeployment={data.deployment}
|
||||
deploymentList={data.deploymentList} />
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Container>
|
||||
|
||||
{#if showRollback}
|
||||
<InstantRollbackDomain
|
||||
bind:show={showRollback}
|
||||
deployment={data.deployment}
|
||||
proxyRuleList={data.proxyRuleList} />
|
||||
{/if}
|
||||
|
||||
@@ -7,14 +7,20 @@ export const load = async ({ params, depends, parent }) => {
|
||||
depends(Dependencies.SITES_DOMAINS);
|
||||
const { site, proxyRuleList } = await parent();
|
||||
|
||||
const [deployment, deploymentList] = await Promise.all([
|
||||
const [deployment, deploymentList, prodReadyDeployments] = await Promise.all([
|
||||
sdk.forProject.sites.getDeployment(params.site, site.deploymentId),
|
||||
sdk.forProject.sites.listDeployments(params.site, [Query.limit(5), Query.orderDesc('')])
|
||||
sdk.forProject.sites.listDeployments(params.site, [Query.limit(5), Query.orderDesc('')]),
|
||||
sdk.forProject.sites.listDeployments(params.site, [
|
||||
Query.limit(1)
|
||||
// Query.equal('status', 'ready')
|
||||
// Query.equal('live', true)
|
||||
])
|
||||
]);
|
||||
return {
|
||||
site,
|
||||
deployment,
|
||||
deploymentList,
|
||||
proxyRuleList
|
||||
proxyRuleList,
|
||||
hasProdReadyDeployments: prodReadyDeployments?.deployments?.length > 0
|
||||
};
|
||||
};
|
||||
|
||||
+2
-2
@@ -13,11 +13,11 @@
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await sdk.forProject.functions.updateDeploymentBuild(
|
||||
await sdk.forProject.sites.updateDeploymentBuild(
|
||||
selectedDeployment.resourceId,
|
||||
selectedDeployment.$id
|
||||
);
|
||||
await invalidate(Dependencies.FUNCTION);
|
||||
await invalidate(Dependencies.SITE);
|
||||
showCancel = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
|
||||
+1
-44
@@ -3,18 +3,13 @@
|
||||
import { Modal } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { processFileList } from '$lib/helpers/files';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { uploader } from '$lib/stores/uploader';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Icon, Layout, Tooltip, Typography, Upload } from '@appwrite.io/pink-svelte';
|
||||
import { createTarGzip } from 'nanotar';
|
||||
import { defaultIgnore } from './store';
|
||||
import ignore from 'ignore';
|
||||
interface FileData {
|
||||
path: string;
|
||||
buffer: ArrayBuffer;
|
||||
}
|
||||
|
||||
export let show = false;
|
||||
export let site: Models.Site;
|
||||
@@ -57,44 +52,6 @@
|
||||
console.log('Time taken to process files:', tock - tick);
|
||||
}
|
||||
|
||||
async function processFileList(files: FileList): Promise<FileData[]> {
|
||||
const fileArray = Array.from(files);
|
||||
const ignorer = ignore();
|
||||
|
||||
// Check for .gitignore file
|
||||
const gitignoreFile = fileArray.find(
|
||||
(file) => file.webkitRelativePath.endsWith('.gitignore') || file.name === '.gitignore'
|
||||
);
|
||||
|
||||
if (gitignoreFile) {
|
||||
const gitignoreContent = await gitignoreFile.text();
|
||||
ignorer.add(gitignoreContent);
|
||||
} else {
|
||||
ignorer.add(defaultIgnore);
|
||||
}
|
||||
|
||||
// Filter files using ignorer
|
||||
const filteredFiles = fileArray.filter((file) => {
|
||||
const path = file.webkitRelativePath || file.name;
|
||||
return !ignorer.ignores(path);
|
||||
});
|
||||
const filePromises = filteredFiles.map(async (file) => {
|
||||
try {
|
||||
const buffer = await file.arrayBuffer();
|
||||
return {
|
||||
path: file?.webkitRelativePath || file.name,
|
||||
buffer: buffer
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(error);
|
||||
console.log(file);
|
||||
error = e;
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(filePromises);
|
||||
}
|
||||
|
||||
async function createDeployment() {
|
||||
try {
|
||||
uploader.uploadSiteDeployment(site.$id, uploadFile);
|
||||
|
||||
@@ -100,146 +100,3 @@ export const columns = writable<Column[]>([
|
||||
format: 'datetime'
|
||||
}
|
||||
]);
|
||||
|
||||
export const defaultIgnore = `
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
`;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { type Models } from '@appwrite.io/console';
|
||||
import { Link } from '$lib/elements';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Card } from '$lib/components';
|
||||
import { Card, Trim } from '$lib/components';
|
||||
|
||||
export let proxyRuleList: Models.ProxyRuleList;
|
||||
|
||||
@@ -33,17 +33,15 @@
|
||||
</Layout.Stack>
|
||||
<Card padding="s" isTile>
|
||||
{#each proxyRuleList?.rules?.slice(0, 3) as rule, i}
|
||||
<Layout.Stack
|
||||
gap="xs"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
direction="row">
|
||||
<Layout.Stack gap="xs">
|
||||
<Layout.Stack alignItems="center" justifyContent="space-between" direction="row">
|
||||
<Layout.Stack gap="xs" inline>
|
||||
<Link variant="quiet-muted" href={`${$protocol}${rule.domain}`} external>
|
||||
{rule.domain}
|
||||
<Trim alternativeTrim>
|
||||
{rule.domain}
|
||||
</Trim>
|
||||
<Icon icon={IconExternalLink} />
|
||||
</Link>
|
||||
<Typography.Text>
|
||||
<Typography.Text truncate>
|
||||
Added {timeFromNow(rule.$createdAt)}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { Card, LabelCard, Modal, Status } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Link } from '$lib/elements';
|
||||
import { Button, InputCheckbox } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Query, type Models } from '@appwrite.io/console';
|
||||
import DeploymentDomains from '../(components)/deploymentDomains.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { Badge, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { timeFromNow, toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { calculateSize } from '$lib/helpers/sizeConvertion';
|
||||
|
||||
export let show = false;
|
||||
export let deployment: Models.Deployment;
|
||||
export let proxyRuleList: Models.ProxyRuleList;
|
||||
export let selectedDeployment: Models.Deployment = undefined;
|
||||
let selectedDeploymentId: string;
|
||||
let prodDeployments: Models.DeploymentList;
|
||||
let confirm = false;
|
||||
|
||||
onMount(async () => {
|
||||
// TODO: re-enable query after backend fixes
|
||||
prodDeployments = await sdk.forProject.sites.listDeployments($page.params.site, [
|
||||
// Query.equal('status', 'ready')
|
||||
// Query.equal('live', true)
|
||||
]);
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await sdk.forProject.sites.updateDeployment(
|
||||
selectedDeployment.resourceId,
|
||||
selectedDeployment.$id
|
||||
);
|
||||
await invalidate(Dependencies.SITE);
|
||||
show = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Deployment has been deleted`
|
||||
});
|
||||
trackEvent(Submit.DeploymentCancel);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.DeploymentCancel);
|
||||
}
|
||||
}
|
||||
|
||||
$: if (selectedDeploymentId) {
|
||||
selectedDeployment = prodDeployments.deployments.find(
|
||||
(deployment) => deployment.$id === selectedDeploymentId
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal title="Instant rollback" bind:show onSubmit={handleSubmit}>
|
||||
<svelte:fragment slot="description">
|
||||
Rollback is only available for deployments that are <b>ready and previously active</b>. <Link
|
||||
href="/#">Learn more</Link
|
||||
>.
|
||||
</svelte:fragment>
|
||||
<Layout.Stack gap="xl">
|
||||
<Layout.Stack gap="s">
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<Typography.Text variant="m-400">Rollback</Typography.Text>
|
||||
<DeploymentDomains domains={proxyRuleList} />
|
||||
</Layout.Stack>
|
||||
<Card padding="s" radius="s" isTile>
|
||||
<Layout.Stack gap="s">
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap="s"
|
||||
justifyContent="space-between">
|
||||
<Typography.Text variant="m-400"
|
||||
>{toLocaleDateTime(deployment.$createdAt)}</Typography.Text>
|
||||
<Badge variant="secondary" content="Current" size="s" />
|
||||
</Layout.Stack>
|
||||
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<Typography.Caption variant="400" color="--color-fgcolor-neutral-secondary">
|
||||
<Status status="ready">{capitalize(deployment.status)}</Status>
|
||||
</Typography.Caption>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="2"
|
||||
height="3"
|
||||
viewBox="0 0 2 3"
|
||||
fill="none">
|
||||
<circle cx="1" cy="1.5" r="1" fill="currentColor" />
|
||||
</svg>
|
||||
<Typography.Caption variant="400" color="--color-fgcolor-neutral-secondary">
|
||||
{calculateSize(deployment.size)}
|
||||
</Typography.Caption>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="2"
|
||||
height="3"
|
||||
viewBox="0 0 2 3"
|
||||
fill="none">
|
||||
<circle cx="1" cy="1.5" r="1" fill="currentColor" />
|
||||
</svg>
|
||||
<Typography.Caption variant="400" color="--color-fgcolor-neutral-secondary">
|
||||
Created {timeFromNow(deployment.$createdAt)}
|
||||
{#if deployment?.providerCommitAuthorUrl && deployment?.providerCommitAuthor}
|
||||
by <Link href={deployment.providerCommitAuthorUrl} external
|
||||
>{deployment.providerCommitAuthor}</Link>
|
||||
{/if}
|
||||
</Typography.Caption>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="s">
|
||||
<Typography.Text variant="m-400">To the following deployment:</Typography.Text>
|
||||
{#if prodDeployments?.deployments?.length}
|
||||
{#each prodDeployments.deployments as prodDeployment}
|
||||
<LabelCard
|
||||
padding="s"
|
||||
radius="s"
|
||||
title={toLocaleDateTime(prodDeployment.$createdAt)}
|
||||
bind:group={selectedDeploymentId}
|
||||
value={prodDeployment.$id}>
|
||||
<Layout.Stack gap="s">
|
||||
<Typography.Caption
|
||||
variant="400"
|
||||
color="--color-fgcolor-neutral-secondary">
|
||||
Created {timeFromNow(prodDeployment.$createdAt)}
|
||||
{#if prodDeployment?.providerCommitAuthorUrl && prodDeployment?.providerCommitAuthor}
|
||||
by <Link href={prodDeployment.providerCommitAuthorUrl} external
|
||||
>{prodDeployment.providerCommitAuthor}</Link>
|
||||
{/if}
|
||||
</Typography.Caption>
|
||||
</Layout.Stack>
|
||||
</LabelCard>
|
||||
{/each}
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{#if selectedDeployment?.$id}
|
||||
<InputCheckbox
|
||||
id="rollbakc"
|
||||
label={`Rollback to ${toLocaleDateTime(selectedDeployment.$createdAt)}, making it the active deployment`}
|
||||
bind:checked={confirm} />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button secondary submit disabled={!selectedDeployment?.$id || !confirm}>Rollback</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -1,10 +1,64 @@
|
||||
<script>
|
||||
import { Container } from '$lib/layout';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { loading } from '$routes/store';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
loading.set(false);
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<slot />
|
||||
</Container>
|
||||
<div class="auth-bg">
|
||||
<div>
|
||||
<Container>
|
||||
<slot />
|
||||
</Container>
|
||||
</div>
|
||||
<footer>
|
||||
<!-- TODO: change text style to eyebrow -->
|
||||
<Typography.Text size="s" color="--color-fgColor-neutral-secondary">
|
||||
POWERED BY
|
||||
</Typography.Text>
|
||||
{#if $app.themeInUse === 'dark'}
|
||||
<img
|
||||
src="/console/images/appwrite-logo-dark.svg"
|
||||
width="120"
|
||||
height="22"
|
||||
alt="Appwrite Logo" />
|
||||
{:else}
|
||||
<img
|
||||
src="/console/images/appwrite-logo-light.svg"
|
||||
width="120"
|
||||
height="22"
|
||||
alt="Appwrite Logo" />
|
||||
{/if}
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.auth-bg {
|
||||
position: fixed;
|
||||
background: var(--color-bgcolor-neutral-primary, #fff);
|
||||
background-size: cover;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: space-between;
|
||||
div {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
footer {
|
||||
padding: 2rem 1rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,227 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Card } from '$lib/components';
|
||||
import Modal from '$lib/components/modal.svelte';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Link } from '$lib/elements';
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
InputChoice,
|
||||
InputEmail,
|
||||
InputPassword,
|
||||
InputText
|
||||
} from '$lib/elements/forms';
|
||||
import { logout } from '$lib/helpers/logout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { redirectTo } from '$routes/store';
|
||||
import { ID, OAuthProvider } from '@appwrite.io/console';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Layout, Spinner, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let data;
|
||||
onMount(async () => {
|
||||
//check if user has access
|
||||
//if not, redirect to login
|
||||
|
||||
let state: 'register' | 'login' | 'authenticated' = 'login';
|
||||
let hasAccess = false;
|
||||
let mail: string;
|
||||
let pass: string;
|
||||
let terms = false;
|
||||
let name: string;
|
||||
|
||||
onMount(() => {
|
||||
if (data?.account?.$id) {
|
||||
//Check if they have access
|
||||
|
||||
state = 'authenticated';
|
||||
}
|
||||
//simulate login
|
||||
setTimeout(() => {
|
||||
goto(`${base}/auth/preview/access`);
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
async function login() {
|
||||
try {
|
||||
await sdk.forConsole.account.createEmailPasswordSession(mail, pass);
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
trackEvent(Submit.AccountLogin);
|
||||
if ($user) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Successfully logged in.'
|
||||
});
|
||||
}
|
||||
if ($redirectTo) {
|
||||
window.location.href = $redirectTo;
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.AccountLogin);
|
||||
}
|
||||
}
|
||||
|
||||
async function register() {
|
||||
try {
|
||||
await sdk.forConsole.account.create(ID.unique(), mail, pass, name ?? '');
|
||||
await sdk.forConsole.account.createEmailPasswordSession(mail, pass);
|
||||
|
||||
if ($redirectTo) {
|
||||
window.location.href = $redirectTo;
|
||||
return;
|
||||
}
|
||||
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
trackEvent(Submit.AccountCreate);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.AccountCreate);
|
||||
}
|
||||
}
|
||||
|
||||
function onGithubAuth() {
|
||||
let url = window.location.origin;
|
||||
|
||||
if ($page.url.searchParams) {
|
||||
const redirect = $page.url.searchParams.get('redirect');
|
||||
$page.url.searchParams.delete('redirect');
|
||||
if (redirect) {
|
||||
url = `${redirect}${$page.url.search}`;
|
||||
} else {
|
||||
url = `${base}${$page.url.search ?? ''}`;
|
||||
}
|
||||
}
|
||||
sdk.forConsole.account.createOAuth2Session(
|
||||
OAuthProvider.Github,
|
||||
window.location.origin + url,
|
||||
window.location.origin,
|
||||
['read:user', 'user:email']
|
||||
);
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (state === 'login') {
|
||||
await login();
|
||||
} else if (state === 'register') {
|
||||
await register();
|
||||
}
|
||||
}
|
||||
|
||||
async function requestAccess() {
|
||||
//TODO: Implement request access
|
||||
}
|
||||
|
||||
$: console.log(data);
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title={state === 'authenticated' ? 'Request access' : 'Sign up or log in to view this preview'}
|
||||
dismissible={false}
|
||||
show
|
||||
hideFooter={state !== 'authenticated'}>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Layout.Stack gap="l">
|
||||
{#if state === 'login'}
|
||||
<InputEmail
|
||||
id="email"
|
||||
label="Email"
|
||||
placeholder="Email"
|
||||
autofocus
|
||||
required
|
||||
bind:value={mail} />
|
||||
<InputPassword
|
||||
id="password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
required
|
||||
meter={false}
|
||||
showPasswordButton
|
||||
bind:value={pass} />
|
||||
<Button size="m" fullWidth submit>Sign in</Button>
|
||||
{#if isCloud}
|
||||
<span class="with-separators eyebrow-heading-3">or</span>
|
||||
<Button size="m" secondary fullWidth on:click={onGithubAuth}>
|
||||
<span class="icon-github" aria-hidden="true" />
|
||||
<span class="text">Sign in with GitHub</span>
|
||||
</Button>
|
||||
{/if}
|
||||
<Button size="m" text fullWidth on:click={() => (state = 'register')}>
|
||||
Sign up instead
|
||||
</Button>
|
||||
{:else if state === 'register'}
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="Your name"
|
||||
autofocus
|
||||
required
|
||||
bind:value={name} />
|
||||
<InputEmail
|
||||
id="email"
|
||||
label="Email"
|
||||
placeholder="Email"
|
||||
required
|
||||
bind:value={mail} />
|
||||
<InputPassword
|
||||
id="password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
required
|
||||
meter={false}
|
||||
showPasswordButton
|
||||
bind:value={pass} />
|
||||
<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/privacy"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
Privacy Policy</a>
|
||||
and accept our
|
||||
<a
|
||||
class="link"
|
||||
href="https://appwrite.io/terms"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">General Terms of Use</a
|
||||
>.</InputChoice>
|
||||
<Button size="m" fullWidth submit>Sign up</Button>
|
||||
{#if isCloud}
|
||||
<span class="with-separators eyebrow-heading-3">or</span>
|
||||
<Button size="m" secondary fullWidth on:click={onGithubAuth}>
|
||||
<span class="icon-github" aria-hidden="true" />
|
||||
<span class="text">Continue with GitHub</span>
|
||||
</Button>
|
||||
{/if}
|
||||
<Button size="m" text fullWidth on:click={() => (state = 'login')}>
|
||||
Sign in instead
|
||||
</Button>
|
||||
{:else if state === 'authenticated'}
|
||||
<Card isTile>
|
||||
<Layout.Stack gap="xxs">
|
||||
<p>
|
||||
You're signed in as <b>{data.account.email}</b>
|
||||
</p>
|
||||
<Link
|
||||
size="s"
|
||||
on:click={() => {
|
||||
logout();
|
||||
state = 'login';
|
||||
}}>Swith account</Link>
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Form>
|
||||
<svelte:head>
|
||||
<title>Preview - Appwrite</title>
|
||||
</svelte:head>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button>Request access</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
<Layout.Stack alignItems="center" justifyContent="center">
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="center">
|
||||
<Spinner size="l" />
|
||||
<Typography.Text>Authenticating...</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<Typography.Title>Please wait while we authenticate you</Typography.Title>
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { base } from '$app/paths';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export const load = async ({ parent }) => {
|
||||
const { account } = await parent();
|
||||
|
||||
return {
|
||||
account
|
||||
};
|
||||
if (!account) {
|
||||
redirect(303, `${base}/auth/preview/access`);
|
||||
} else {
|
||||
return {
|
||||
account
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Card } from '$lib/components';
|
||||
import Modal from '$lib/components/modal.svelte';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Link } from '$lib/elements';
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
InputChoice,
|
||||
InputEmail,
|
||||
InputPassword,
|
||||
InputText
|
||||
} from '$lib/elements/forms';
|
||||
import { logout } from '$lib/helpers/logout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { redirectTo } from '$routes/store';
|
||||
import { ID, OAuthProvider } from '@appwrite.io/console';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import BGDark from './bg_dark.jpg';
|
||||
import BGLight from './bg_light.jpg';
|
||||
import { app } from '$lib/stores/app.js';
|
||||
|
||||
export let data;
|
||||
|
||||
let state: 'register' | 'login' | 'authenticated' = 'login';
|
||||
let mail: string;
|
||||
let pass: string;
|
||||
let terms = false;
|
||||
let name: string;
|
||||
|
||||
onMount(() => {
|
||||
if (data?.account?.$id) {
|
||||
state = 'authenticated';
|
||||
}
|
||||
});
|
||||
|
||||
async function login() {
|
||||
try {
|
||||
await sdk.forConsole.account.createEmailPasswordSession(mail, pass);
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
trackEvent(Submit.AccountLogin);
|
||||
if ($user) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Successfully logged in.'
|
||||
});
|
||||
}
|
||||
if ($redirectTo) {
|
||||
window.location.href = $redirectTo;
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.AccountLogin);
|
||||
}
|
||||
}
|
||||
|
||||
async function register() {
|
||||
try {
|
||||
await sdk.forConsole.account.create(ID.unique(), mail, pass, name ?? '');
|
||||
await sdk.forConsole.account.createEmailPasswordSession(mail, pass);
|
||||
|
||||
if ($redirectTo) {
|
||||
window.location.href = $redirectTo;
|
||||
return;
|
||||
}
|
||||
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
trackEvent(Submit.AccountCreate);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.AccountCreate);
|
||||
}
|
||||
}
|
||||
|
||||
function onGithubAuth() {
|
||||
let url = window.location.origin;
|
||||
|
||||
if ($page.url.searchParams) {
|
||||
const redirect = $page.url.searchParams.get('redirect');
|
||||
$page.url.searchParams.delete('redirect');
|
||||
if (redirect) {
|
||||
url = `${redirect}${$page.url.search}`;
|
||||
} else {
|
||||
url = `${base}${$page.url.search ?? ''}`;
|
||||
}
|
||||
}
|
||||
sdk.forConsole.account.createOAuth2Session(
|
||||
OAuthProvider.Github,
|
||||
window.location.origin + url,
|
||||
window.location.origin,
|
||||
['read:user', 'user:email']
|
||||
);
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (state === 'login') {
|
||||
await login();
|
||||
} else if (state === 'register') {
|
||||
await register();
|
||||
}
|
||||
}
|
||||
|
||||
async function requestAccess() {
|
||||
//TODO: Implement request access
|
||||
}
|
||||
|
||||
$: console.log(data);
|
||||
</script>
|
||||
|
||||
{#if $app.themeInUse === 'dark'}
|
||||
<img class="preview-img" src={BGDark} alt="background" aria-hidden="true" />
|
||||
{:else}
|
||||
<img class="preview-img" src={BGLight} alt="background" aria-hidden="true" />
|
||||
{/if}
|
||||
|
||||
<svelte:head>
|
||||
<title>Preview access - Appwrite</title>
|
||||
</svelte:head>
|
||||
|
||||
<Modal
|
||||
title={state === 'authenticated' ? 'Request access' : 'Sign up or log in to view this preview'}
|
||||
dismissible={false}
|
||||
show
|
||||
hideFooter={state !== 'authenticated'}>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Layout.Stack gap="l">
|
||||
{#if state === 'login'}
|
||||
<InputEmail
|
||||
id="email"
|
||||
label="Email"
|
||||
placeholder="Email"
|
||||
autofocus
|
||||
required
|
||||
bind:value={mail} />
|
||||
<InputPassword
|
||||
id="password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
required
|
||||
bind:value={pass} />
|
||||
<Button size="m" fullWidth submit>Sign in</Button>
|
||||
{#if isCloud}
|
||||
<span class="with-separators eyebrow-heading-3">or</span>
|
||||
<Button size="m" secondary fullWidth on:click={onGithubAuth}>
|
||||
<span class="icon-github" aria-hidden="true" />
|
||||
<span class="text">Sign in with GitHub</span>
|
||||
</Button>
|
||||
{/if}
|
||||
<Button size="m" text fullWidth on:click={() => (state = 'register')}>
|
||||
Sign up instead
|
||||
</Button>
|
||||
{:else if state === 'register'}
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="Your name"
|
||||
autofocus
|
||||
required
|
||||
bind:value={name} />
|
||||
<InputEmail
|
||||
id="email"
|
||||
label="Email"
|
||||
placeholder="Email"
|
||||
required
|
||||
bind:value={mail} />
|
||||
<InputPassword
|
||||
id="password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
required
|
||||
bind:value={pass} />
|
||||
<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/privacy"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
Privacy Policy</a>
|
||||
and accept our
|
||||
<a
|
||||
class="link"
|
||||
href="https://appwrite.io/terms"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">General Terms of Use</a
|
||||
>.</InputChoice>
|
||||
<Button size="m" fullWidth submit>Sign up</Button>
|
||||
{#if isCloud}
|
||||
<span class="with-separators eyebrow-heading-3">or</span>
|
||||
<Button size="m" secondary fullWidth on:click={onGithubAuth}>
|
||||
<span class="icon-github" aria-hidden="true" />
|
||||
<span class="text">Continue with GitHub</span>
|
||||
</Button>
|
||||
{/if}
|
||||
<Button size="m" text fullWidth on:click={() => (state = 'login')}>
|
||||
Sign in instead
|
||||
</Button>
|
||||
{:else if state === 'authenticated'}
|
||||
<Card isTile>
|
||||
<Layout.Stack gap="xxs">
|
||||
<p>
|
||||
You're signed in as <b>{data.account.email}</b>
|
||||
</p>
|
||||
<Link
|
||||
size="s"
|
||||
on:click={() => {
|
||||
logout();
|
||||
state = 'login';
|
||||
}}>Swith account</Link>
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Form>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button on:click={requestAccess}>Request access</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
<style lang="scss">
|
||||
.preview-img {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: top left;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
export const load = async ({ parent }) => {
|
||||
const { account } = await parent();
|
||||
|
||||
return {
|
||||
account
|
||||
};
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user