Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/lib/components/tracker-dialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<Dialog.Content>
<Dialog.Title>Notice</Dialog.Title>
<Dialog.Description>
We use Posthog to track errors and usage to improve EduTools. Please disable your tracker/ad
blocker to allow this. Don't worry, we won't add any ads.
We use Posthog to detect/fix errors, track usage and roll out features. Please disable your
tracker/ad blocker to allow this. Don't worry, we won't show you any ads.
</Dialog.Description>
<Dialog.Footer>
<Dialog.Close onclick={() => ($trackerDialogClosed = true)}>
Expand Down
6 changes: 1 addition & 5 deletions src/routes/handoff/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,7 @@
<Button
variant="outline"
onclick={() => {
if (document.referrer && new URL(document.referrer).origin === window.location.origin) {
history.back();
} else {
location.href = '/';
}
history.back();
}}>Cancel</Button
>
<Button
Expand Down
87 changes: 81 additions & 6 deletions src/routes/mirrors/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,81 @@
<iframe
src="https://educationaltools.github.io/status/"
title="Status"
frameborder="0"
class="h-full w-full"
></iframe>
<script lang="ts">
import Badge from '$lib/components/ui/badge/badge.svelte';
import Button from '$lib/components/ui/button/button.svelte';
import * as Card from '$lib/components/ui/card/index.js';
import createBackup from '$lib/createBackup';
import Trophy from '@lucide/svelte/icons/trophy';
import Star from '@lucide/svelte/icons/star';
import TriangleAlert from '@lucide/svelte/icons/triangle-alert';
import { page } from '$app/state';
import clsx from 'clsx';
import type { Mirror } from './mirrors.config';
import { mirrors } from './mirrors.config';
import { toast } from 'svelte-sonner';
</script>

<div class="mx-auto flex w-full max-w-3xl flex-col gap-3 p-3">
<h1 class="text-3xl">Mirrors</h1>
<div class="grid w-full grid-cols-1 gap-3 md:grid-cols-2">
{#each mirrors as mirror}
{@const selected = (() => {
try {
return page.url.hostname == new URL(mirror.url).hostname;
} catch (e) {
return false;
}
})()}
<Card.Root class={clsx(selected && 'bg-neutral-200 dark:bg-neutral-700')}>
<Card.Header>
<Card.Title>
{(() => {
try {
return new URL(mirror.url).hostname;
} catch (e) {
return 'Invalid URL';
}
})()}
</Card.Title>

{#if selected}
<Badge variant="outline">Current</Badge>
{/if}
{#if mirror.quality == 'highlyrecommended'}
<Badge class="bg-gradient-to-r from-yellow-300 to-yellow-500"
><Trophy />Highly Recommended</Badge
>
{:else if mirror.quality == 'recommended'}
<Badge class="bg-yellow-600 dark:bg-yellow-200"><Star />Recommended</Badge>
{:else if mirror.quality == 'notrecommended'}
<Badge class="bg-red-600 dark:bg-red-200"><TriangleAlert />Not Recommended</Badge>
{/if}

{#if mirror.notes}
<Card.Description>{mirror.notes}</Card.Description>
{/if}
</Card.Header>
{#if !selected}
<Card.Footer class="flex flex-row gap-3">
<Button variant="outline" href={mirror.url}>Go</Button>
<Button
onclick={() => {
try {
const backup = createBackup();
const url = new URL('/handoff', mirror.url);
url.searchParams.set('data', backup);
location.href = url.toString();
} catch (error) {
console.error('Migration failed:', error);
toast.error('Migration failed');
}
}}
>
Migrate
</Button>
Comment on lines +61 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify backup data size for URL parameter limitations.

Passing backup data via URL query parameters may hit browser URL length limits (typically 2048-8192 characters). Large backups could fail silently or be truncated.

Consider validating the backup size or using an alternative method for large data transfers:

Alternative approach for large backups:

 onclick={() => {
     try {
         const backup = createBackup();
+        // Check backup size before URL encoding
+        const encodedBackup = encodeURIComponent(backup);
+        if (encodedBackup.length > 2000) {
+            toast.error('Backup data too large for migration. Please export and import manually.');
+            return;
+        }
         const url = new URL('/handoff', mirror.url);
         url.searchParams.set('data', backup);
         location.href = url.toString();
     } catch (error) {
         console.error('Migration failed:', error);
         toast.error('Migration failed');
     }
 }}

🏁 Script executed:

#!/bin/bash
# Check if createBackup function has any size validation or if there are any tests that verify large backup handling
rg -A 10 "createBackup" --type ts --type js --type svelte

# Look for any existing URL length validation
rg -i "url.*length|length.*limit|max.*length" --type ts --type js --type svelte

Length of output: 201


🏁 Script executed:

#!/bin/bash
set -e

echo "1. Searching for createBackup definitions and references in .ts, .js, and .svelte files"
rg -n "createBackup" -g "*.ts" -g "*.js" -g "*.svelte" || echo "No matches for createBackup"

echo
echo "2. Searching for any length, size, or limit checks in .ts, .js, and .svelte files"
rg -n -i "length|size|limit" -g "*.ts" -g "*.js" -g "*.svelte" || echo "No length/size/limit validations found"

Length of output: 263771


Implement Backup Size Handling Before URL Navigation

A review of createBackup() usages in src/routes/.../+page.svelte and src/lib/createBackup.ts shows no existing data‐size validation or URL‐length safeguards. Large backups risk exceeding browser URL limits, causing silent failures.

Please add logic before location.href to:

  • Encode and measure the backup payload (e.g. via encodeURIComponent(...))
  • If its length exceeds a safe threshold (e.g. 2000 characters), show an error and skip navigation
  • Otherwise proceed as before

Example diff for src/routes/mirrors/+page.svelte:

 onclick={() => {
     try {
-        const backup = createBackup();
+        const backup = createBackup();
+        const encoded = encodeURIComponent(backup);
+        if (encoded.length > 2000) {
+            toast.error('Backup too large for migration. Please export/import manually.');
+            return;
+        }
         const url = new URL('/handoff', mirror.url);
         url.searchParams.set('data', backup);
         location.href = url.toString();
     } catch (error) {
         console.error('Migration failed:', error);
         toast.error('Migration failed');
     }
 }}

Apply similar checks in all code paths that construct a migration URL.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Button
onclick={() => {
try {
const backup = createBackup();
const url = new URL('/handoff', mirror.url);
url.searchParams.set('data', backup);
location.href = url.toString();
} catch (error) {
console.error('Migration failed:', error);
toast.error('Migration failed');
}
}}
>
Migrate
</Button>
<Button
onclick={() => {
try {
const backup = createBackup();
const encoded = encodeURIComponent(backup);
if (encoded.length > 2000) {
toast.error('Backup too large for migration. Please export/import manually.');
return;
}
const url = new URL('/handoff', mirror.url);
url.searchParams.set('data', backup);
location.href = url.toString();
} catch (error) {
console.error('Migration failed:', error);
toast.error('Migration failed');
}
}}
>
Migrate
</Button>
🤖 Prompt for AI Agents
In src/routes/mirrors/+page.svelte around lines 61 to 75, the backup data
created by createBackup() is directly appended to the URL without checking its
size, risking exceeding browser URL length limits. To fix this, encode the
backup string using encodeURIComponent, measure its length, and if it exceeds a
safe threshold like 2000 characters, display an error toast and do not proceed
with navigation. Only set location.href if the encoded backup size is within the
limit. Apply this size check and error handling in all similar migration URL
constructions in this file.

</Card.Footer>
{/if}
</Card.Root>
{/each}
</div>
</div>
15 changes: 15 additions & 0 deletions src/routes/mirrors/mirrors.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type Mirror = {
url: string;
quality?: 'highlyrecommended' | 'recommended' | 'notrecommended';
notes?: string;
};

export const mirrors: Mirror[] = [
{ url: 'https://edutools.ingo.au', quality: 'highlyrecommended' },
{ url: 'https://edutools.ingowolf.au', quality: 'recommended' },
{ url: 'https://educationaltools.github.io', quality: 'recommended' },
{ url: 'https://educationaltools.vercel.app' },
{ url: 'https://edutools-d915e.web.app' },
{ url: 'https://edutools-d915e.firebaseapp.com' },
{ url: 'https://edutools.infinityfreeapp.com', quality: 'notrecommended' }
];
Loading