diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 4c63fd7..8960ae0 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -1,9 +1,8 @@ import fs from "node:fs"; -import { IncomingMessage, ServerResponse } from "node:http"; -import https from "node:https"; +import type { IncomingMessage, ServerResponse } from "node:http"; import path from "node:path"; -import { Connect, Plugin, ViteDevServer } from "vite"; -import { defineConfig, MarkdownOptions } from "vitepress"; +import type { Connect, Plugin, ViteDevServer } from "vite"; +import { defineConfig, type MarkdownOptions } from "vitepress"; const host = process.env.TAURI_DEV_HOST; @@ -130,68 +129,34 @@ function audioProxyPlugin(): Plugin { }; } -function proxyAudio( +async function proxyAudio( req: Connect.IncomingMessage, res: ServerResponse, next: Connect.NextFunction, ) { - const url = new URL( - `https://www.howtostudykorean.com/wp-content/uploads${req.url}`, - ); - - // Forward common headers (important for Range streaming) - const headers = { - ...req.headers, - host: url.host, - range: req.headers["range"], - }; + const url = new URL(`https://www.howtostudykorean.com/wp-content${req.url}`); const segs = req.url!.slice(1).split("/"); const filename = segs.pop()!; const saveroot = "docs/public"; - const savetemp = path.join(saveroot, ...segs, filename + ".temp"); const saveto = path.join(saveroot, ...segs, filename); if (fs.existsSync(saveto)) { - next(); - return; + return next(); } fs.mkdirSync(path.join(saveroot, ...segs), { recursive: true }); - const fileStream = fs.createWriteStream(savetemp); - const upstreamReq = https.request( - { - protocol: url.protocol, - hostname: url.hostname, - port: url.port || undefined, - path: url.pathname + url.search, - method: req.method, - headers, - }, - (upstreamRes) => { - // Pass status (200 or 206 for Range) - res.writeHead(upstreamRes.statusCode || 200, upstreamRes.headers); + const r = await fetch(url, { redirect: "follow" }); + if (!r.ok) { + console.error(r); + return next(); + } - // Stream through - upstreamRes.pipe(res); - upstreamRes.pipe(fileStream); - }, - ); + fs.writeFileSync(saveto, Buffer.from(await r.arrayBuffer())); + // console.log(fs.statSync(saveto)); + console.log("saved to", saveto); - fileStream.on("finish", () => { - fs.renameSync(savetemp, saveto); - console.log("saved to", saveto); - }); - - fileStream.on("error", (err) => { - console.error("write failed:", err); - }); - - upstreamReq.on("error", (err) => { - res.statusCode = 502; - res.end(`Bad Gateway: ${err.message}`); - }); - - req.pipe(upstreamReq); + res.setHeader("Content-Type", "audio/mpeg"); + fs.createReadStream(saveto).pipe(res); } diff --git a/docs/.vitepress/theme/components/AudioButton.vue b/docs/.vitepress/theme/components/AudioButton.vue index a3d8f8e..43ea694 100644 --- a/docs/.vitepress/theme/components/AudioButton.vue +++ b/docs/.vitepress/theme/components/AudioButton.vue @@ -30,9 +30,11 @@ async function playAudio() { const uploadURL = "https://www.howtostudykorean.com/wp-content/uploads"; let src = props.src; if (src.startsWith(uploadURL)) { - src = src.replace(uploadURL, "/uploads/"); + src = src.replace(uploadURL, "/uploads"); audioEl.value.onerror = () => { if (!audioEl.value) return; + console.log(audioEl.value.error, src); + audioEl.value.src = props.src; audioEl.value.currentTime = 0; audioEl.value.play(); diff --git a/package.json b/package.json index 4275d6f..60526ab 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "@supabase/supabase-js": "^2.110.5", + "@types/node": "^24.13.3", "pdfjs-dist": "^5.7.284", "tauri-plugin-tts-api": "^0.1.11", "vite": "^8.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8338c7a..ab5e6c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@supabase/supabase-js': specifier: ^2.110.5 version: 2.110.5 + '@types/node': + specifier: ^24.13.3 + version: 24.13.3 pdfjs-dist: specifier: ^5.7.284 version: 5.7.284 @@ -19,7 +22,7 @@ importers: version: 0.1.11 vite: specifier: ^8.1.5 - version: 8.1.5(esbuild@0.28.1)(yaml@2.9.0) + version: 8.1.5(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0) yaml: specifier: ^2.9.0 version: 2.9.0 @@ -32,7 +35,7 @@ importers: version: 2.11.4 vitepress: specifier: 2.0.0-alpha.17 - version: 2.0.0-alpha.17(lightningcss@1.32.0)(postcss@8.5.19)(yaml@2.9.0) + version: 2.0.0-alpha.17(@types/node@24.13.3)(lightningcss@1.32.0)(postcss@8.5.19)(yaml@2.9.0) vue: specifier: ^3.5.39 version: 3.5.39 @@ -710,6 +713,9 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/node@24.13.3': + resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -1078,6 +1084,9 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} @@ -1665,16 +1674,20 @@ snapshots: '@types/mdurl@2.0.0': {} + '@types/node@24.13.3': + dependencies: + undici-types: 7.18.2 + '@types/unist@3.0.3': {} '@types/web-bluetooth@0.0.21': {} '@ungap/structured-clone@1.3.2': {} - '@vitejs/plugin-vue@6.0.7(vite@7.3.6(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.39)': + '@vitejs/plugin-vue@6.0.7(vite@7.3.6(@types/node@24.13.3)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.39)': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 7.3.6(lightningcss@1.32.0)(yaml@2.9.0) + vite: 7.3.6(@types/node@24.13.3)(lightningcss@1.32.0)(yaml@2.9.0) vue: 3.5.39 '@vue/compiler-core@3.5.39': @@ -2070,6 +2083,8 @@ snapshots: tslib@2.8.1: {} + undici-types@7.18.2: {} + unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -2103,7 +2118,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@7.3.6(lightningcss@1.32.0)(yaml@2.9.0): + vite@7.3.6(@types/node@24.13.3)(lightningcss@1.32.0)(yaml@2.9.0): dependencies: esbuild: 0.28.1 fdir: 6.5.0(picomatch@4.0.5) @@ -2112,11 +2127,12 @@ snapshots: rollup: 4.62.2 tinyglobby: 0.2.17 optionalDependencies: + '@types/node': 24.13.3 fsevents: 2.3.3 lightningcss: 1.32.0 yaml: 2.9.0 - vite@8.1.5(esbuild@0.28.1)(yaml@2.9.0): + vite@8.1.5(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.5 @@ -2124,11 +2140,12 @@ snapshots: rolldown: 1.1.5 tinyglobby: 0.2.17 optionalDependencies: + '@types/node': 24.13.3 esbuild: 0.28.1 fsevents: 2.3.3 yaml: 2.9.0 - vitepress@2.0.0-alpha.17(lightningcss@1.32.0)(postcss@8.5.19)(yaml@2.9.0): + vitepress@2.0.0-alpha.17(@types/node@24.13.3)(lightningcss@1.32.0)(postcss@8.5.19)(yaml@2.9.0): dependencies: '@docsearch/css': 4.6.3 '@docsearch/js': 4.6.3 @@ -2138,7 +2155,7 @@ snapshots: '@shikijs/transformers': 3.23.0 '@shikijs/types': 3.23.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 6.0.7(vite@7.3.6(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.39) + '@vitejs/plugin-vue': 6.0.7(vite@7.3.6(@types/node@24.13.3)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.39) '@vue/devtools-api': 8.1.5 '@vue/shared': 3.5.39 '@vueuse/core': 14.3.0(vue@3.5.39) @@ -2147,7 +2164,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.2.0 shiki: 3.23.0 - vite: 7.3.6(lightningcss@1.32.0)(yaml@2.9.0) + vite: 7.3.6(@types/node@24.13.3)(lightningcss@1.32.0)(yaml@2.9.0) vue: 3.5.39 optionalDependencies: postcss: 8.5.19