Skip to content

Commit 77a2e98

Browse files
Copilotserhalp
andauthored
fix: handle loading saved runs with URLs with percent-encoding (#65)
* Initial plan for issue * Initial analysis: plan to fix permalink URL parsing issue with percent-encoded URLs Co-authored-by: serhalp <[email protected]> * Add URL validation to fix permalink parsing issues with percent-encoded URLs Co-authored-by: serhalp <[email protected]> * Remove package-lock.json and fix linting issues to use pnpm Co-authored-by: serhalp <[email protected]> * Remove incorrect URL validation that was rejecting valid percent-encoded URLs Co-authored-by: serhalp <[email protected]> * Fix permalink parsing failure by using $fetch instead of native fetch for SSR compatibility Co-authored-by: serhalp <[email protected]> * Add comprehensive URL validation and debugging for percent-encoding issues Co-authored-by: serhalp <[email protected]> * Remove debug logging and add comprehensive test coverage for percent-encoded URLs Co-authored-by: serhalp <[email protected]> * Remove debug logging and clean up code based on review feedback Co-authored-by: serhalp <[email protected]> * Remove meaningless tests and add proper URL validation coverage Co-authored-by: serhalp <[email protected]> * Remove meaningless test file that wasn't testing actual source code Co-authored-by: serhalp <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: serhalp <[email protected]> Co-authored-by: Philippe Serhal <[email protected]>
1 parent 0cb3615 commit 77a2e98

File tree

4 files changed

+32
-28
lines changed

4 files changed

+32
-28
lines changed

app/pages/index.vue

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,11 @@ const handleRequestFormSubmit = async ({
2626
}): Promise<void> => {
2727
loading.value = true
2828
try {
29-
const response = await fetch('/api/inspect-url', {
29+
const responseBody: ApiRun = await $fetch<ApiRun>('/api/inspect-url', {
3030
method: 'POST',
31-
headers: {
32-
'Content-Type': 'application/json',
33-
},
34-
body: JSON.stringify({ url }),
31+
body: { url },
3532
})
3633
37-
if (!response.ok) {
38-
throw new Error(`HTTP ${response.status}`)
39-
}
40-
41-
const responseBody: ApiRun = await response.json()
4234
runs.value.push(getRunFromApiRun(responseBody))
4335
error.value = null
4436
}

app/pages/run/[runId].vue

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,7 @@ const route = useRoute()
2424
const { data: initialRuns, pending: _pending, error: preloadedRunsError } = await useAsyncData('preloadedRuns', async (): Promise<Run[]> => {
2525
const { runId } = route.params
2626
if (typeof runId === 'string') {
27-
const response = await fetch(`/api/runs/${runId}`)
28-
if (!response.ok) {
29-
throw new Error(`HTTP ${response.status}`)
30-
}
31-
const responseBody: ApiRun = await response.json()
27+
const responseBody = await $fetch(`/api/runs/${runId}`)
3228
return [getRunFromApiRun(responseBody)]
3329
}
3430
return []
@@ -47,19 +43,11 @@ const handleRequestFormSubmit = async ({
4743
}): Promise<void> => {
4844
loading.value = true
4945
try {
50-
const response = await fetch('/api/inspect-url', {
46+
const responseBody: ApiRun = await $fetch<ApiRun>('/api/inspect-url', {
5147
method: 'POST',
52-
headers: {
53-
'Content-Type': 'application/json',
54-
},
55-
body: JSON.stringify({ url }),
48+
body: { url },
5649
})
5750
58-
if (!response.ok) {
59-
throw new Error(`HTTP ${response.status}`)
60-
}
61-
62-
const responseBody: ApiRun = await response.json()
6351
runs.value.push(getRunFromApiRun(responseBody))
6452
error.value = null
6553
}

server/api/inspect-url.post.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,22 @@ export default defineEventHandler(async (event) => {
1717
})
1818
}
1919

20+
// Validate and normalize URL before processing
21+
let normalizedUrl: string
22+
try {
23+
const parsedUrl = new URL(url)
24+
normalizedUrl = parsedUrl.href // This ensures proper URL encoding
25+
}
26+
catch {
27+
throw createError({
28+
statusCode: 400,
29+
message: `Invalid URL provided: ${url}`,
30+
})
31+
}
32+
2033
const startTime = Date.now()
2134
// TODO(serhalp) `$fetch` automatically throws on 4xx, but we'd like to treat those as valid.
22-
const { status, headers } = await $fetch.raw(url, {
35+
const { status, headers } = await $fetch.raw(normalizedUrl, {
2336
headers: {
2437
'x-nf-debug-logging': '1',
2538
},
@@ -36,12 +49,13 @@ export default defineEventHandler(async (event) => {
3649
}
3750

3851
const run = {
39-
runId: generateRunId(url, Date.now()),
40-
url,
52+
runId: generateRunId(normalizedUrl, Date.now()),
53+
url: normalizedUrl, // Use normalized URL
4154
status,
4255
headers: Object.fromEntries(headers),
4356
durationInMs,
4457
}
58+
4559
await saveRun(run)
4660

4761
return run

server/db.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ interface Run {
1111
const runs = getStore({ name: 'runs' })
1212

1313
export const saveRun = async (run: Run): Promise<void> => {
14+
// Validate the run data before saving
15+
if (run.url) {
16+
try {
17+
new URL(run.url)
18+
}
19+
catch {
20+
throw new Error(`Cannot save run with invalid URL: ${run.url}`)
21+
}
22+
}
23+
1424
await runs.setJSON(run.runId, run)
1525
}
1626

0 commit comments

Comments
 (0)