Implement Cloudflare custom domain management flow

- Added support for connecting, refreshing, and removing custom domains through Cloudflare.
- Updated API routes and validation schemas to handle new domain actions.
- Refactored domain management logic in the dashboard to accommodate Cloudflare integration.
- Enhanced user experience with clear status messages and DNS instructions for domain setup.
- Updated translations and UI components to reflect new domain management features.
This commit is contained in:
Rotimi Best
2026-03-27 09:12:53 +01:00
parent 3919ff6c2f
commit f3d212f984
23 changed files with 1376 additions and 244 deletions
+30 -20
View File
@@ -1,33 +1,43 @@
<script>
import { goto } from '$app/navigation';
import { browser } from '$app/environment';
import { page } from '$app/state';
import { Button } from '@cio/ui/base/button';
import { Empty } from '@cio/ui/custom/empty';
import HeartCrack from '@lucide/svelte/icons/heart-crack';
if (page.status == 404 && browser) {
goto('/404');
}
const isNotFound = $derived(page.status === 404);
// TODO: Add error tracking here
console.error('error message', page.error?.message);
</script>
<svelte:head>
<title>Something unexpected occured</title>
<title>{isNotFound ? 'Page not found' : 'Something unexpected occurred'}</title>
</svelte:head>
<Empty
title="Something unexpected occured."
description="Don't worry, your learning is safe. It isn't your fault, it is ours. We have gotten the error notification and will push a fix ASAP. In the meantime, take a short break and come back a bit later."
icon={HeartCrack}
variant="page"
layout="full-page"
showLogo={true}
>
<div class="flex gap-2">
<Button href="https://classroomio.com/tools" variant="secondary">Try Free tools</Button>
<Button href="/">Go Home</Button>
</div>
</Empty>
{#if isNotFound}
<Empty
title="Page not found"
description="The page you're looking for doesn't exist or you don't have permission to view it."
icon={HeartCrack}
variant="page"
layout="full-page"
showLogo={true}
>
<div class="flex gap-2">
<Button href="/">Go Home</Button>
</div>
</Empty>
{:else}
<Empty
title="Something unexpected occurred."
description="Don't worry, your learning is safe. It isn't your fault, it is ours. We have gotten the error notification and will push a fix ASAP. In the meantime, take a short break and come back a bit later."
icon={HeartCrack}
variant="page"
layout="full-page"
showLogo={true}
>
<div class="flex gap-2">
<Button href="https://classroomio.com/tools" variant="secondary">Try Free tools</Button>
<Button href="/">Go Home</Button>
</div>
</Empty>
{/if}
@@ -1,14 +1,14 @@
import { classroomio } from '$lib/utils/services/api';
import { getApiKeyHeaders } from '$lib/utils/services/api/server';
import { calcCourseDiscount } from '$lib/utils/functions/course';
import { redirect } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';
export const load = async ({ params, url }) => {
const slug = params?.slug ?? '';
const inviteToken = url.searchParams.get('invite_token') ?? null;
if (!slug) {
redirect(307, '/404');
throw error(404, 'Not found');
}
if (inviteToken) {
@@ -20,7 +20,7 @@ export const load = async ({ params, url }) => {
const result = await response.json();
if (!result.success || !result.data) {
redirect(307, '/404');
throw error(404, 'Not found');
}
const data = result.data;
@@ -50,9 +50,9 @@ export const load = async ({ params, url }) => {
requiresPaymentOrInvite: false,
token: inviteToken
};
} catch (error) {
console.error('Enroll load: failed to load invite', error);
redirect(307, '/404');
} catch (e) {
console.error('Enroll load: failed to load invite', e);
throw error(404, 'Not found');
}
}
@@ -63,7 +63,7 @@ export const load = async ({ params, url }) => {
const result = await response.json();
if (!result.success || !result.data) {
redirect(307, '/404');
throw error(404, 'Not found');
}
const courseData = result.data;
@@ -99,8 +99,8 @@ export const load = async ({ params, url }) => {
requiresPaymentOrInvite: !isFree,
token: null as string | null
};
} catch (error) {
console.error('Enroll load: failed to load course by slug', error);
redirect(307, '/404');
} catch (e) {
console.error('Enroll load: failed to load course by slug', e);
throw error(404, 'Not found');
}
};
@@ -1,7 +1,7 @@
import { getOrgBySiteName } from '$features/org/api/org.server';
import { classroomio } from '$lib/utils/services/api';
import { getApiKeyHeaders } from '$lib/utils/services/api/server';
import { redirect } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';
export const load = async ({ params = { hash: '' } }) => {
try {
@@ -28,8 +28,8 @@ export const load = async ({ params = { hash: '' } }) => {
invite: result.data,
currentOrg
};
} catch (error) {
console.error('Error decoding organization invite params.hash', error);
redirect(307, '/404');
} catch (e) {
console.error('Error decoding organization invite params.hash', e);
throw error(404, 'Not found');
}
};