fix: Enhance audiobook API validation and prevent critical meta file deletion
This commit is contained in:
parent
f947ece01a
commit
33d49d6966
8 changed files with 277 additions and 270 deletions
2
.github/workflows/playwright.yml
vendored
2
.github/workflows/playwright.yml
vendored
|
|
@ -1,7 +1,7 @@
|
|||
name: Playwright Tests
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master, 'v*.*.*' ]
|
||||
branches: [ main, master ]
|
||||
pull_request:
|
||||
branches: [ main, master ]
|
||||
jobs:
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ docker run --name openreader-webui \
|
|||
-v /path/to/your/library:/app/docstore/library:ro \
|
||||
ghcr.io/richardr1126/openreader-webui:latest
|
||||
```
|
||||
Seperate from the docstore volume, this will mount an external folder to `/app/docstore/library` (read-only recommended). This allows you to connect OpenReader to an existing library of documents.
|
||||
Separate from the main docstore volume, this mounts an external folder into the container at `/app/docstore/library` (read-only recommended) so OpenReader can use an existing library of documents.
|
||||
|
||||
To import from the mounted library: **Settings → Documents → Server Library Import**
|
||||
|
||||
|
|
|
|||
10
package.json
10
package.json
|
|
@ -14,8 +14,8 @@
|
|||
"@types/howler": "^2.2.12",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vercel/analytics": "^1.6.1",
|
||||
"cmpstr": "^3.0.4",
|
||||
"compromise": "^14.14.4",
|
||||
"cmpstr": "^3.1.1",
|
||||
"compromise": "^14.14.5",
|
||||
"core-js": "^3.47.0",
|
||||
"dexie": "^4.2.1",
|
||||
"dexie-react-hooks": "^4.2.0",
|
||||
|
|
@ -41,10 +41,10 @@
|
|||
"@eslint/eslintrc": "^3.3.3",
|
||||
"@playwright/test": "^1.57.0",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@types/node": "^20.19.26",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/node": "^20.19.30",
|
||||
"@types/react": "^19.2.8",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-next": "^15.5.9",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.19",
|
||||
|
|
|
|||
518
pnpm-lock.yaml
518
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -203,7 +203,8 @@ export async function POST(request: NextRequest) {
|
|||
existingSettings?.format ??
|
||||
incomingSettings?.format ??
|
||||
requestedFormat;
|
||||
const postSpeed = incomingSettings?.postSpeed ?? existingSettings?.postSpeed ?? 1;
|
||||
const rawPostSpeed = incomingSettings?.postSpeed ?? existingSettings?.postSpeed ?? 1;
|
||||
const postSpeed = Number.isFinite(Number(rawPostSpeed)) ? Number(rawPostSpeed) : 1;
|
||||
|
||||
// Use provided chapter index or find the next available index robustly (handles gaps)
|
||||
let chapterIndex: number;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,16 @@ function getAudiobooksRootDir(request: NextRequest): string {
|
|||
return safe ? join(AUDIOBOOKS_V1_DIR, safe) : AUDIOBOOKS_V1_DIR;
|
||||
}
|
||||
|
||||
const SAFE_ID_REGEX = /^[a-zA-Z0-9._-]{1,128}$/;
|
||||
|
||||
function isSafeId(value: string): boolean {
|
||||
return SAFE_ID_REGEX.test(value);
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const bookId = request.nextUrl.searchParams.get('bookId');
|
||||
if (!bookId) {
|
||||
if (!bookId || !isSafeId(bookId)) {
|
||||
return NextResponse.json({ error: 'Missing bookId parameter' }, { status: 400 });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -512,7 +512,7 @@ async function normalizeAudiobookDirectoryChapterLayout(intermediateDir: string)
|
|||
if (/^\d+-input\.mp3$/i.test(file)) {
|
||||
await unlink(path.join(intermediateDir, file)).catch(() => {});
|
||||
}
|
||||
if (file.endsWith('.meta.json')) {
|
||||
if (file.endsWith('.meta.json') && file !== 'audiobook.meta.json') {
|
||||
await unlink(path.join(intermediateDir, file)).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ async function expectChaptersBackendState(page: Page, bookId: string) {
|
|||
|
||||
async function resetAudiobookById(page: Page, bookId: string) {
|
||||
const res = await page.request.delete(`/api/audiobook?bookId=${bookId}`);
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.ok() || res.status() === 404).toBeTruthy();
|
||||
}
|
||||
|
||||
async function resetAudiobookIfPresent(page: Page) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue