mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
fix: stuff
This commit is contained in:
1 parent
6c7b35620e
commit
6da5eadbaa
66 files changed
+6850
-10095
No files matched your search
@@ -7,3 +7,4 @@ node_modules
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
/playwright-report
|
||||
+6
-3
@@ -1,5 +1,8 @@
|
||||
FROM node:20-alpine as build
|
||||
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
WORKDIR /app
|
||||
|
||||
ADD ./build.js /app/build.js
|
||||
@@ -7,7 +10,7 @@ ADD ./tsconfig.json /app/tsconfig.json
|
||||
ADD ./svelte.config.js /app/svelte.config.js
|
||||
ADD ./vite.config.ts /app/vite.config.ts
|
||||
ADD ./package.json /app/package.json
|
||||
ADD ./package-lock.json /app/package-lock.json
|
||||
ADD ./pnpm-lock.yaml /app/pnpm-lock.yaml
|
||||
ADD ./src /app/src
|
||||
ADD ./static /app/static
|
||||
|
||||
@@ -21,8 +24,8 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
|
||||
ENV VITE_CONSOLE_MODE=$VITE_CONSOLE_MODE
|
||||
ENV VITE_STRIPE_PUBLIC_KEY=$VITE_STRIPE_PUBLIC_KEY
|
||||
|
||||
RUN npm ci
|
||||
RUN npm run build
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
||||
RUN pnpm run build
|
||||
|
||||
FROM nginx:1.25-alpine
|
||||
|
||||
|
||||
@@ -7,6 +7,15 @@ services:
|
||||
VITE_APPWRITE_ENDPOINT: ${VITE_APPWRITE_ENDPOINT}
|
||||
VITE_APPWRITE_GROWTH_ENDPOINT: ${VITE_APPWRITE_GROWTH_ENDPOINT}
|
||||
VITE_STRIPE_PUBLIC_KEY: ${VITE_STRIPE_PUBLIC_KEY}
|
||||
develop:
|
||||
watch:
|
||||
- action: rebuild
|
||||
path: ./
|
||||
ignore:
|
||||
- .github
|
||||
- tests/
|
||||
- node_modules/
|
||||
- build/
|
||||
environment:
|
||||
- VITE_CONSOLE_MODE
|
||||
- VITE_APPWRITE_ENDPOINT
|
||||
|
||||
Generated
-9901
File diff suppressed because it is too large.
Load diff
+3
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@appwrite/console",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">=20"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
@@ -74,5 +74,6 @@
|
||||
"vite": "^5.0.11",
|
||||
"vitest": "^1.2.1"
|
||||
},
|
||||
"type": "module"
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903"
|
||||
}
|
||||
@@ -5,7 +5,7 @@ const config: PlaywrightTestConfig = {
|
||||
reportSlowTests: null,
|
||||
reporter: [['html', { open: 'never' }]],
|
||||
use: {
|
||||
baseURL: 'http://localhost:4173/console'
|
||||
baseURL: 'http://localhost:4173/console/'
|
||||
},
|
||||
webServer: {
|
||||
timeout: 120000,
|
||||
|
||||
Generated
+6636
File diff suppressed because it is too large.
Load diff
@@ -21,7 +21,7 @@ export enum View {
|
||||
export function getView(url: URL, route: Page['route'], fallback: View): View {
|
||||
return (url.searchParams.get('view') ?? preferences.get(route).view) === View.Grid
|
||||
? View.Grid
|
||||
: View.Table ?? fallback;
|
||||
: (View.Table ?? fallback);
|
||||
}
|
||||
|
||||
export function getColumns(route: Page['route'], fallback: string[]): string[] {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
export let coupon: Coupon = null;
|
||||
|
||||
$: selectedCampaign = campaigns.get(coupon?.campaign ?? campaign);
|
||||
$: variation = (coupon?.campaign ?? campaign ? selectedCampaign?.template : 'default') as
|
||||
$: variation = ((coupon?.campaign ?? campaign) ? selectedCampaign?.template : 'default') as
|
||||
| 'default'
|
||||
| CampaignData['template'];
|
||||
|
||||
|
||||
@@ -71,8 +71,8 @@ export const feedbackData = createFeedbackDataStore();
|
||||
|
||||
function createFeedbackStore() {
|
||||
const { subscribe, update } = writable<Feedback>({
|
||||
elapsed: browser ? parseInt(localStorage.getItem('feedbackElapsed')) ?? 0 : 0,
|
||||
visualized: browser ? parseInt(localStorage.getItem('feedbackVisualized')) ?? 0 : 0,
|
||||
elapsed: browser ? (parseInt(localStorage.getItem('feedbackElapsed')) ?? 0) : 0,
|
||||
visualized: browser ? (parseInt(localStorage.getItem('feedbackVisualized')) ?? 0) : 0,
|
||||
notification: false,
|
||||
type: 'general',
|
||||
show: false
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { page } from '$app/stores';
|
||||
import { derived } from 'svelte/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
export const user = derived(
|
||||
page,
|
||||
($page) => $page.data.account as Models.User<Record<string, string>>
|
||||
);
|
||||
export type Account = Models.User<{ organization?: string } & Record<string, string>>;
|
||||
|
||||
export const user = derived(page, ($page) => {
|
||||
console.log($page.data.account);
|
||||
if (browser) sessionStorage.setItem('account', JSON.stringify($page.data.account));
|
||||
return $page.data.account as Account;
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { base } from '$app/paths';
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const load: LayoutLoad = async ({ parent }) => {
|
||||
const { account } = await parent();
|
||||
|
||||
if (!account) {
|
||||
redirect(303, base);
|
||||
}
|
||||
};
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -11,7 +11,8 @@
|
||||
import { newOrgModal, organization } from '$lib/stores/organization';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { afterUpdate, onMount } from 'svelte';
|
||||
import { loading, requestedMigration } from '../store';
|
||||
import { loading } from '$routes/store';
|
||||
import { requestedMigration } from '../store';
|
||||
import Create from './createOrganization.svelte';
|
||||
import {
|
||||
showUsageRatesModal,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { isCloud } from '$lib/system';
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const load: LayoutLoad = async ({ fetch, depends, parent }) => {
|
||||
await parent(); // ensure user is authenticated before proceeding
|
||||
await parent();
|
||||
|
||||
depends(Dependencies.RUNTIMES);
|
||||
depends(Dependencies.CONSOLE_VARIABLES);
|
||||
|
||||
@@ -8,12 +8,10 @@ export const load: LayoutLoad = async ({ depends }) => {
|
||||
depends(Dependencies.FACTORS);
|
||||
depends(Dependencies.IDENTITIES);
|
||||
|
||||
const promises = [
|
||||
const [factors, identities] = await Promise.all([
|
||||
sdk.forConsole.account.listMfaFactors(),
|
||||
sdk.forConsole.account.listIdentities()
|
||||
];
|
||||
|
||||
const [factors, identities] = await Promise.all(promises);
|
||||
]);
|
||||
|
||||
return {
|
||||
factors,
|
||||
|
||||
@@ -2,12 +2,17 @@ import { Dependencies } from '$lib/constants';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ parent, depends }) => {
|
||||
await parent();
|
||||
export const load: PageLoad = async ({ depends }) => {
|
||||
depends(Dependencies.PAYMENT_METHODS);
|
||||
depends(Dependencies.ADDRESS);
|
||||
|
||||
const [paymentMethods, addressList] = await Promise.all([
|
||||
sdk.forConsole.billing.listPaymentMethods(),
|
||||
sdk.forConsole.billing.listAddresses()
|
||||
]);
|
||||
|
||||
return {
|
||||
paymentMethods: await sdk.forConsole.billing.listPaymentMethods(),
|
||||
addressList: await sdk.forConsole.billing.listAddresses()
|
||||
paymentMethods,
|
||||
addressList
|
||||
};
|
||||
};
|
||||
@@ -32,16 +32,22 @@ export const load: LayoutLoad = async ({ params, depends }) => {
|
||||
|
||||
try {
|
||||
const prefs = await sdk.forConsole.account.getPrefs();
|
||||
const newPrefs = { ...prefs, organization: params.organization };
|
||||
sdk.forConsole.account.updatePrefs(newPrefs);
|
||||
preferences.loadTeamPrefs(params.organization);
|
||||
if (prefs.organization !== params.organization) {
|
||||
const newPrefs = { ...prefs, organization: params.organization };
|
||||
sdk.forConsole.account.updatePrefs(newPrefs);
|
||||
}
|
||||
|
||||
const [organization, members] = await Promise.all([
|
||||
sdk.forConsole.teams.get(params.organization) as Promise<Organization>,
|
||||
sdk.forConsole.teams.listMemberships(params.organization),
|
||||
preferences.loadTeamPrefs(params.organization)
|
||||
]);
|
||||
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
organization: await (sdk.forConsole.teams.get(
|
||||
params.organization
|
||||
) as Promise<Organization>),
|
||||
members: await sdk.forConsole.teams.listMemberships(params.organization)
|
||||
organization,
|
||||
members
|
||||
};
|
||||
} catch (e) {
|
||||
const prefs = await sdk.forConsole.account.getPrefs();
|
||||
|
||||
@@ -12,23 +12,24 @@ export const load: PageLoad = async ({ parent, depends }) => {
|
||||
depends(Dependencies.INVOICES);
|
||||
depends(Dependencies.ADDRESS);
|
||||
|
||||
let billingAddress: Address = null;
|
||||
const billingAddressId = (organization as Organization)?.billingAddressId;
|
||||
if (billingAddressId) {
|
||||
try {
|
||||
billingAddress = await sdk.forConsole.billing.getOrganizationBillingAddress(
|
||||
organization.$id,
|
||||
billingAddressId
|
||||
);
|
||||
} catch {
|
||||
billingAddress = null;
|
||||
}
|
||||
}
|
||||
const billingAddressPromise: Promise<Address> = billingAddressId
|
||||
? sdk.forConsole.billing
|
||||
.getOrganizationBillingAddress(organization.$id, billingAddressId)
|
||||
.catch(() => null)
|
||||
: null;
|
||||
|
||||
const [paymentMethods, addressList, aggregationList, billingAddress] = await Promise.all([
|
||||
sdk.forConsole.billing.listPaymentMethods(),
|
||||
sdk.forConsole.billing.listAddresses(),
|
||||
sdk.forConsole.billing.listAggregation(organization.$id),
|
||||
billingAddressPromise
|
||||
]);
|
||||
|
||||
return {
|
||||
paymentMethods: await sdk.forConsole.billing.listPaymentMethods(),
|
||||
addressList: await sdk.forConsole.billing.listAddresses(),
|
||||
aggregationList: await sdk.forConsole.billing.listAggregation(organization.$id),
|
||||
paymentMethods,
|
||||
addressList,
|
||||
aggregationList,
|
||||
billingAddress
|
||||
};
|
||||
};
|
||||
@@ -4,12 +4,16 @@ import { sdk } from '$lib/stores/sdk';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import { isCloud } from '$lib/system';
|
||||
|
||||
export const load: PageLoad = async ({ depends, parent }) => {
|
||||
const { organization } = await parent();
|
||||
export const load: PageLoad = async ({ depends, params }) => {
|
||||
depends(Dependencies.ORGANIZATION);
|
||||
|
||||
const [projects, invoices] = await Promise.all([
|
||||
sdk.forConsole.projects.list([Query.equal('teamId', params.organization)]),
|
||||
isCloud ? sdk.forConsole.billing.listInvoices(params.organization) : undefined
|
||||
]);
|
||||
|
||||
return {
|
||||
projects: await sdk.forConsole.projects.list([Query.equal('teamId', organization.$id)]),
|
||||
invoices: isCloud ? await sdk.forConsole.billing.listInvoices(organization.$id) : undefined
|
||||
projects,
|
||||
invoices
|
||||
};
|
||||
};
|
||||
@@ -10,6 +10,7 @@ export const load: PageLoad = async ({ url, route }) => {
|
||||
const search = getSearch(url);
|
||||
const limit = getLimit(url, route, PAGE_LIMIT);
|
||||
const offset = pageToOffset(page, limit);
|
||||
|
||||
if (typeof url.searchParams.get('create') === 'string') {
|
||||
showCreateUser.set(true);
|
||||
}
|
||||
|
||||
@@ -9,11 +9,16 @@ export const load: LayoutLoad = async ({ params, depends }) => {
|
||||
depends(Dependencies.USER);
|
||||
|
||||
try {
|
||||
const [user, userFactors] = await Promise.all([
|
||||
sdk.forProject.users.get(params.user),
|
||||
sdk.forProject.users.listMfaFactors(params.user)
|
||||
]);
|
||||
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
user: await sdk.forProject.users.get(params.user),
|
||||
userFactors: await sdk.forProject.users.listMfaFactors(params.user)
|
||||
user,
|
||||
userFactors
|
||||
};
|
||||
} catch (e) {
|
||||
error(e.code, e.message);
|
||||
|
||||
+7
-7
@@ -10,17 +10,17 @@ import { Query } from '@appwrite.io/console';
|
||||
export const load: LayoutLoad = async ({ params, depends }) => {
|
||||
depends(Dependencies.COLLECTION);
|
||||
try {
|
||||
const [collection, allCollections] = await Promise.all([
|
||||
sdk.forProject.databases.getCollection(params.database, params.collection),
|
||||
sdk.forProject.databases.listCollections(params.database, [Query.orderDesc('')])
|
||||
]);
|
||||
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
collection: await sdk.forProject.databases.getCollection(
|
||||
params.database,
|
||||
params.collection
|
||||
),
|
||||
subNavigation: SubNavigation,
|
||||
allCollections: await sdk.forProject.databases.listCollections(params.database, [
|
||||
Query.orderDesc('')
|
||||
])
|
||||
collection,
|
||||
allCollections
|
||||
};
|
||||
} catch (e) {
|
||||
error(e.code, e.message);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
import { parseExpression } from 'cron-parser';
|
||||
import { onMount } from 'svelte';
|
||||
import { functionsList } from './store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -59,6 +60,10 @@
|
||||
wizard.showCover(Initial);
|
||||
}
|
||||
|
||||
function getNextScheduledExecution(func: Models.Function) {
|
||||
return toLocaleDateTime(parseExpression(func.schedule, { utc: true }).next().toString());
|
||||
}
|
||||
|
||||
$: $registerCommands([
|
||||
{
|
||||
label: 'Create function',
|
||||
@@ -108,7 +113,7 @@
|
||||
aria-hidden="true"
|
||||
use:tooltip={{
|
||||
content: `Next execution:
|
||||
${toLocaleDateTime(parseExpression(func.schedule, { utc: true }).next().toString())}`
|
||||
${getNextScheduledExecution(func)}`
|
||||
}} />
|
||||
</li>
|
||||
{/if}
|
||||
|
||||
@@ -11,15 +11,20 @@ export const load: LayoutLoad = async ({ params, depends }) => {
|
||||
depends(Dependencies.DEPLOYMENTS);
|
||||
|
||||
try {
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
function: await sdk.forProject.functions.get(params.function),
|
||||
proxyRuleList: await sdk.forProject.proxy.listRules([
|
||||
const [func, proxyRuleList] = await Promise.all([
|
||||
sdk.forProject.functions.get(params.function),
|
||||
sdk.forProject.proxy.listRules([
|
||||
Query.equal('resourceType', 'function'),
|
||||
Query.equal('resourceId', params.function),
|
||||
Query.limit(1)
|
||||
])
|
||||
]);
|
||||
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
function: func,
|
||||
proxyRuleList
|
||||
};
|
||||
} catch (e) {
|
||||
error(e.code, e.message);
|
||||
|
||||
@@ -9,13 +9,15 @@ export const load: PageLoad = async ({ depends, url }) => {
|
||||
const limit = PAGE_LIMIT;
|
||||
const offset = Number(url.searchParams.get('offset') ?? 0);
|
||||
|
||||
const [variables, installations] = await Promise.all([
|
||||
sdk.forProject.projectApi.listVariables(),
|
||||
sdk.forProject.vcs.listInstallations([Query.limit(limit), Query.offset(offset)])
|
||||
]);
|
||||
|
||||
return {
|
||||
limit,
|
||||
offset,
|
||||
variables: await sdk.forProject.projectApi.listVariables(),
|
||||
installations: await sdk.forProject.vcs.listInstallations([
|
||||
Query.limit(limit),
|
||||
Query.offset(offset)
|
||||
])
|
||||
variables,
|
||||
installations
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { base } from '$app/paths';
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const load: LayoutLoad = async ({ parent }) => {
|
||||
const { account } = await parent();
|
||||
|
||||
if (account) {
|
||||
redirect(303, base);
|
||||
}
|
||||
};
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+16
-67
@@ -8,12 +8,13 @@
|
||||
import { app } from '$lib/stores/app';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { onMount } from 'svelte';
|
||||
import Loading from './loading.svelte';
|
||||
import { loading, requestedMigration } from './store';
|
||||
import { requestedMigration } from './store';
|
||||
import { parseIfString } from '$lib/helpers/object';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { campaigns } from '$lib/stores/campaigns';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { loading } from '$routes/store';
|
||||
import Loading from './loading.svelte';
|
||||
|
||||
onMount(async () => {
|
||||
// handle sources
|
||||
@@ -40,79 +41,27 @@
|
||||
requestedMigration.set(parseIfString(migrateData) as Record<string, string>);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle initial load.
|
||||
*/
|
||||
|
||||
function shouldRedirect(route: string, routes: string[]) {
|
||||
return !routes.some((n) => route.startsWith(n));
|
||||
}
|
||||
|
||||
const authenticationRoutes = ['/auth', '/git'];
|
||||
const acceptedUnauthenticatedRoutes = [
|
||||
'/login',
|
||||
'/register',
|
||||
'/recover',
|
||||
'/invite',
|
||||
'/card',
|
||||
'/hackathon',
|
||||
'/mfa'
|
||||
];
|
||||
const acceptedAuthenticatedRoutes = [
|
||||
'/console',
|
||||
'/invite',
|
||||
'/card',
|
||||
'/hackathon',
|
||||
'/recover'
|
||||
];
|
||||
|
||||
const pathname = $page.url.pathname;
|
||||
const user = $page.data.account as Models.User<Record<string, string>>;
|
||||
|
||||
if ($page.url.searchParams.has('code')) {
|
||||
const code = $page.url.searchParams.get('code');
|
||||
try {
|
||||
const couponData = await sdk.forConsole.billing.getCoupon(code);
|
||||
if (couponData?.campaign && campaigns.has(couponData.campaign)) {
|
||||
if (user) {
|
||||
goto(`${base}/apply-credit?code=${code}`);
|
||||
loading.set(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
if ($page.url.searchParams.has('campaign')) {
|
||||
const campaign = $page.url.searchParams.get('campaign');
|
||||
if (campaigns.has(campaign)) {
|
||||
if (user) {
|
||||
goto(`${base}/apply-credit?campaign=${campaign}`);
|
||||
const coupon = await sdk.forConsole.billing.getCoupon(code).catch<null>(() => null);
|
||||
if (coupon?.campaign && campaigns.has(coupon.campaign)) {
|
||||
if ($user) {
|
||||
goto(`${base}/apply-credit?code=${code}`);
|
||||
loading.set(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRedirect(pathname, authenticationRoutes)) {
|
||||
if (user?.$id) {
|
||||
if (shouldRedirect(pathname, acceptedAuthenticatedRoutes)) {
|
||||
await goto(base, {
|
||||
replaceState: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (acceptedUnauthenticatedRoutes.some((n) => pathname.startsWith(n))) {
|
||||
await goto(`${base}${pathname}${$page.url.search}`);
|
||||
} else {
|
||||
await goto(`${base}/login`, {
|
||||
replaceState: true
|
||||
});
|
||||
}
|
||||
if (user && $page.url.searchParams.has('campaign')) {
|
||||
const campaign = $page.url.searchParams.get('campaign');
|
||||
if (campaigns.has(campaign)) {
|
||||
goto(`${base}/apply-credit?campaign=${campaign}`);
|
||||
loading.set(false);
|
||||
return;
|
||||
}
|
||||
loading.set(false);
|
||||
}
|
||||
|
||||
loading.set(false);
|
||||
});
|
||||
|
||||
afterNavigate((navigation) => {
|
||||
|
||||
+26
-28
@@ -7,49 +7,47 @@ import { Dependencies } from '$lib/constants';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import { redirectTo } from './store';
|
||||
import { base } from '$app/paths';
|
||||
import type { Account } from '$lib/stores/user';
|
||||
import type { AppwriteException } from '@appwrite.io/console';
|
||||
|
||||
export const ssr = false;
|
||||
|
||||
export const load: LayoutLoad = async ({ depends, url }) => {
|
||||
export const load: LayoutLoad = async ({ depends, url, route }) => {
|
||||
depends(Dependencies.ACCOUNT);
|
||||
|
||||
const [account, error] = (await sdk.forConsole.account
|
||||
.get()
|
||||
.then((response) => [response, null])
|
||||
.catch((error) => [null, error])) as [Account, AppwriteException];
|
||||
|
||||
if (url.searchParams.has('forceRedirect')) {
|
||||
redirectTo.set(url.searchParams.get('forceRedirect') || null);
|
||||
url.searchParams.delete('forceRedirect');
|
||||
}
|
||||
|
||||
try {
|
||||
const account = await sdk.forConsole.account.get<{ organization?: string }>();
|
||||
|
||||
if (account) {
|
||||
return {
|
||||
account,
|
||||
account: account,
|
||||
organizations: await sdk.forConsole.teams.list()
|
||||
};
|
||||
} catch (error) {
|
||||
const acceptedRoutes = [
|
||||
'/login',
|
||||
'/register',
|
||||
'/recover',
|
||||
'/invite',
|
||||
'/auth/magic-url',
|
||||
'/auth/oauth2/success',
|
||||
'/auth/oauth2/failure',
|
||||
'/card',
|
||||
'/hackathon',
|
||||
'/mfa'
|
||||
];
|
||||
}
|
||||
|
||||
const redirectUrl = url.pathname && url.pathname !== '/' ? `redirect=${url.pathname}` : '';
|
||||
const path = url.search ? `${url.search}&${redirectUrl}` : `?${redirectUrl}`;
|
||||
const isPublicRoute = route.id?.startsWith('/(public)');
|
||||
if (url.pathname && !isPublicRoute) {
|
||||
url.searchParams.set('redirect', url.pathname);
|
||||
}
|
||||
|
||||
if (error.type === 'user_more_factors_required') {
|
||||
if (url.pathname === `${base}/mfa`) return {}; // Ensure any previous account/organizations are cleared
|
||||
redirect(303, `${base}/mfa${path}`);
|
||||
}
|
||||
if (error.type === 'user_more_factors_required') {
|
||||
if (url.pathname === `${base}/mfa`) return; // Ensure any previous account/organizations are cleared
|
||||
redirect(303, withParams(`${base}/mfa`, url.searchParams));
|
||||
}
|
||||
|
||||
if (!acceptedRoutes.some((n) => url.pathname.startsWith(n))) {
|
||||
redirect(303, `${base}/login${path}`);
|
||||
}
|
||||
return {}; // Ensure any previous account/organizations are cleared
|
||||
if (!isPublicRoute) {
|
||||
redirect(303, withParams(`${base}/login`, url.searchParams));
|
||||
}
|
||||
};
|
||||
|
||||
function withParams(pathname: string, searchParams: URLSearchParams) {
|
||||
if (searchParams.size > 0) return `${pathname}?${searchParams.toString()}`;
|
||||
return pathname;
|
||||
}
|
||||
+13
-13
@@ -2,19 +2,19 @@ import { redirect } from '@sveltejs/kit';
|
||||
import { base } from '$app/paths';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ parent, url }) => {
|
||||
export const load: PageLoad = async ({ parent, url, untrack }) => {
|
||||
const { organizations, account } = await parent();
|
||||
|
||||
if (!account) {
|
||||
redirect(302, `${base}/login`);
|
||||
}
|
||||
|
||||
if (organizations.total) {
|
||||
const teamId = account.prefs.organization ?? organizations.teams[0].$id;
|
||||
if (!teamId) {
|
||||
redirect(303, `${base}/account/organizations${url.search ?? ''}`);
|
||||
} else redirect(303, `${base}/organization-${teamId}${url.search ?? ''}`);
|
||||
} else {
|
||||
redirect(303, `${base}/onboarding${url.search ?? ''}`);
|
||||
}
|
||||
untrack(() => {
|
||||
if (organizations.total) {
|
||||
const teamId = account.prefs.organization ?? organizations.teams[0].$id;
|
||||
if (!teamId) {
|
||||
redirect(303, `${base}/account/organizations${url.search}`);
|
||||
} else {
|
||||
redirect(303, `${base}/organization-${teamId}${url.search}`);
|
||||
}
|
||||
} else {
|
||||
redirect(303, `${base}/onboarding${url.search}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const loading = writable(true);
|
||||
export const requestedMigration = writable<Record<string, string> | null>(null);
|
||||
export const redirectTo = writable<string | null>(null);
|
||||
export const loading = writable(true);
|
||||
@@ -8,12 +8,12 @@ test('upgrade - free tier', async ({ page }) => {
|
||||
await createFreeProject(page);
|
||||
await test.step('upgrade project', async () => {
|
||||
await page.getByRole('link', { name: 'upgrade' }).click();
|
||||
await page.waitForURL('/console/organization-**/change-plan');
|
||||
await page.waitForURL('./organization-**/change-plan');
|
||||
await page.locator('input[value="tier-1"]').click();
|
||||
await page.getByRole('button', { name: 'add' }).first().click();
|
||||
await enterCreditCard(page);
|
||||
// skip members
|
||||
await page.getByRole('button', { name: 'change plan' }).click();
|
||||
await page.waitForURL('/console/organization-**');
|
||||
await page.waitForURL('./organization-**');
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@ type Metadata = {
|
||||
export function registerUserStep(page: Page): Promise<Metadata> {
|
||||
return test.step('register user', async () => {
|
||||
const seed = crypto.randomUUID();
|
||||
await page.goto('/register');
|
||||
await page.goto('./register');
|
||||
// await page.getByRole('button', { name: 'only required' }).click();
|
||||
const inputs = {
|
||||
name: page.locator('id=name'),
|
||||
@@ -27,7 +27,7 @@ export function registerUserStep(page: Page): Promise<Metadata> {
|
||||
await inputs.password.fill(values.password);
|
||||
await inputs.terms.check();
|
||||
await page.getByRole('button', { name: 'Sign up', exact: true }).click();
|
||||
await page.waitForURL('/console/onboarding');
|
||||
await page.waitForURL('./onboarding');
|
||||
|
||||
return values;
|
||||
});
|
||||
|
||||
@@ -8,23 +8,23 @@ type Metadata = {
|
||||
|
||||
export async function createFreeProject(page: Page): Promise<Metadata> {
|
||||
const organizationId = await test.step('create organization', async () => {
|
||||
await page.goto('/console');
|
||||
await page.waitForURL('/console/onboarding');
|
||||
await page.goto('./');
|
||||
await page.waitForURL('./onboarding');
|
||||
await page.locator('id=name').fill('test org');
|
||||
await page.locator('id=plan').selectOption('tier-0');
|
||||
await page.getByRole('button', { name: 'get started' }).click();
|
||||
await page.waitForURL('/console/organization-**');
|
||||
await page.waitForURL('./organization-**');
|
||||
return getOrganizationIdFromUrl(page.url());
|
||||
});
|
||||
|
||||
const projectId = await test.step('create project', async () => {
|
||||
await page.waitForURL('/console/organization-**');
|
||||
await page.waitForURL('./organization-**');
|
||||
await page.getByRole('button', { name: 'create project' }).first().click();
|
||||
await page.locator('id=name').fill('test project');
|
||||
await page.getByRole('button', { name: 'next' }).click();
|
||||
await page.locator('label').filter({ hasText: 'Frankfurt' }).click();
|
||||
await page.getByRole('button', { name: 'create' }).click();
|
||||
await page.waitForURL('/console/project-**/overview/platforms');
|
||||
await page.waitForURL('./project-**/overview/platforms');
|
||||
expect(page.url()).toContain('/console/project-');
|
||||
|
||||
return getProjectIdFromUrl(page.url());
|
||||
|
||||
@@ -18,31 +18,31 @@ export async function enterCreditCard(page: Page) {
|
||||
|
||||
export async function createProProject(page: Page): Promise<Metadata> {
|
||||
const organizationId = await test.step('create organization', async () => {
|
||||
await page.goto('/console');
|
||||
await page.waitForURL('/console/onboarding');
|
||||
await page.goto('./');
|
||||
await page.waitForURL('./onboarding');
|
||||
await page.locator('id=name').fill('test org');
|
||||
await page.locator('id=plan').selectOption('tier-1');
|
||||
await page.getByRole('button', { name: 'get started' }).click();
|
||||
await page.waitForURL('/console/create-organization**');
|
||||
await page.waitForURL('./create-organization**');
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
await page.getByRole('button', { name: 'add' }).first().click();
|
||||
await enterCreditCard(page);
|
||||
// skip members
|
||||
await page.getByRole('button', { name: 'create organization' }).click();
|
||||
await page.waitForURL('/console/organization-**');
|
||||
await page.waitForURL('./organization-**');
|
||||
|
||||
return getOrganizationIdFromUrl(page.url());
|
||||
});
|
||||
|
||||
const projectId = await test.step('create project', async () => {
|
||||
await page.waitForURL('/console/organization-**');
|
||||
await page.waitForURL('./organization-**');
|
||||
await page.getByRole('button', { name: 'create project' }).first().click();
|
||||
await page.getByPlaceholder('project name').fill('test project');
|
||||
await page.getByRole('button', { name: 'next' }).click();
|
||||
await page.locator('label').filter({ hasText: 'frankfurt' }).click();
|
||||
await page.getByRole('button', { name: 'create' }).click();
|
||||
await page.waitForURL('/console/project-**/overview/platforms');
|
||||
expect(page.url()).toContain('/console/project-');
|
||||
await page.waitForURL('./project-**/overview/platforms');
|
||||
expect(page.url()).toContain('./project-');
|
||||
|
||||
return getProjectIdFromUrl(page.url());
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user