fix: stuff
@@ -7,3 +7,4 @@ node_modules
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
/playwright-report
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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());
|
||||
});
|
||||
|
||||