From fbeed219fc5ca7bf681fbbb460a898f0f3bfda13 Mon Sep 17 00:00:00 2001 From: tigeren Date: Tue, 2 Sep 2025 17:14:32 +0000 Subject: [PATCH] fix: improve file path handling in API routes - Updated file reading and database querying logic to decode URL-encoded paths, ensuring proper handling of special characters. - Refactored path usage in the GET requests for both media file listing and content retrieval, enhancing compatibility with various file names. --- data/media.db | Bin 126976 -> 126976 bytes src/app/api/files/content/route.ts | 27 +++++++++++++++------------ src/app/api/files/route.ts | 9 ++++++--- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/data/media.db b/data/media.db index ebcebae6bbff1d9a857df7420e755be4c1c7c959..38afa93abdec4749f6d9082f66bb6c863fd6776b 100644 GIT binary patch delta 10670 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 E0bd$bEdT%j delta 11566 zcmeHN3v?URnbxlvjdb-I%d$Li{F2{s{E$XZ%M_AeNFa%mI3a}KIF4+|iEwNO+aWKg zGm=Y70w(Tk88$$m^mSMuC1hEJrM!AzDUVaacDq1N(=7`jq0kW8LZPMY-halH?2r&n zdJfC(AqRf{y7!-Z=f3{$zu!MQe{J0PYvZ0mm_J_B0`m{7`z~+N^M`f(3I6awhxaL7 zK3rM~AfGJFqvbE@_?P&1Y5CK<{F0|QSD(Yw5KmwKuqPHBj*6bv%OCW<$%phr&j`>L zk0qjEola*b8xviF(ZNJ~$UuJ@_+wP+O8GzYAMo$-@9@W%=k+d~XMCR1lIk`)bR)Eqe(?wDy9u;UUoxi8p?%tft24ut4|EnO`(J@d z`N$HdrrgnbI5QkEr3ZY?T0yd9x~I>S9^Aj=d`L9VC2>_zkyk{8(@90HSx+U<-9Y~p z@rU&Z{jK`P^dB;D=D*qN*`FCMG(69haX&ITjH9Mirj+R?gdqv?40+ppz4?eGWqHpU zv%Y0pY5RulkX^Jt!29_1{QcwO3*^1a))@CJKu8D9nZmSy^5M zpuEPw0o?nu~vLdSp1|B?S4|6Bez|2qFG{#E`z_!s%-_^0_N_(%AE;Sccl@ju}2=I`Kd z+OHBe`x*g&&0vg^0q7!h@m_MFWa@6m=+Oqo_qO z3q=izY7|u{Dp6FRC`VC-!i}PoWkyU;f@(2}A{2!vW}+xSF$0BwA|HhdMIH(#id+;9 z6gen(6m}Fg6jl@#6lN3zg$acb1&3n9fRaVQpwKgniJ0j$eNKyGnX)Yv-w_J6eRuy@$!*vssU?WpY;+da13wm#cZn`m=d z-?zSOeb{=N^*U>(b)L1tYOox$JZrhva+77i(q{2nT;@NTUo}5szQa6Xj+!kkW)t}h zd4cRFd+379B=VkTOX&Z_5Tde39|~%J0{Evi=r3Yjei)8#9rk&B!3J+rgI{!uzGh#j zSq%B8%?wUe#jNVhs6kIwZOp0-88uL!RqHaUe|A=_&8WUv8C9&ws?{0QTa{HSCsl8x zBCD2X)TXkG8g^&YP-#}hIZ?;v4Hak9U{O{r%&38xS+yXe`e$TSA*<$RRG%xOig{Vp znN@Q$s@IWKb2OF0Z-mdP_KezO%cx;%R<&f*kU66Uarx;mG0re$Rbxi=b6M4pQGIMy zWl*(X3!=V=75qeg=~?G$?Kkx^6SzI|OWgjV;&wp>w?|wWGQ9Qqz&68bcO%S#T!ZtzJCJ5UEVVfXq6NGJouuTxQ3Boo( z*d_?u1Yw&XY!ifSg0M{xwh6-a2tnK?2-^fixgl&SbO%S#T!ZtzJCJ5UEVVfXq z6NGJouuTxQ3Boo(*d_?u1Yw&XY!ifSg0M{xwh6*ELD(iRLUEe_MG1niO%S#T!ZsmV z82k{^%hj44wN4TgV2ByuPDxmj<% z()>^6CoHX&Tda-NG3)Pb35p``*zNXh_TTUwH0pX}auMYC$=kWQxva#1GG_~%$IYfu zUJWRnt}~A%4L%^Aluq*GFD0Fv&ri~ z{$bl8L#3O!m^N?}iVPJ30tls_px1dBW``~;i zmODRKM?>i+KG?<;E?^}G4ZwYnudFdZy>X^wVUq=*++PLPLxIeHI3H#xxBVByK#(8# z@F~cbcYah1E_vTaK|@|W(*a7^lQ0XMGW=;3jiK*90!syli}Ap*vE9dSez4)qhoqwq z|8ryCra+GT_@8bBUU~ADaEZ|_<+Di|NIRZ~aT-E5eB4Px=$k%{(GdE?FJLnbq4)d( zIt-+eq49Z6JGj9lzx|goFe<-#6|SPuc;BnAi8Bnc(t41kg?%)NUh}tVV3gZmq1%^E zcrm_u+YD`C58w(2`~mntAJX5$G%zo-v)MZhjfP!Z9d|wVH{&ZNvuUO27NRFTWRF=i zk6D^6`>i$B2W^$M4x4Pxwckp^;&%QA^U@*1ve&>%?dn)wnvtTwtOJuTUxS!`L6fKsKNfqS@;<+Qj^GklkU z2vMoY~)kIufsmS=g9oHV(( z1qR!fYz1XvkVW}lVJp)6OMv<`WP?gD^uaZvu3TsTiZx|!H@(O`YPgMM^j88IAAdJD zxx`8Nlx;#shEGw}(i^FA>67p=wqbRNsp?#$MOp<(%+Mn0oj?7|kE!dLv~oGn>U=>e zLo1aFN;{r{Et-MTn=H+tJuMPESw`u}F?wL|5Gc<)4R5FoS-qn?k0ek~NET=eSB5=E zN1p-eFeiDgNQ&68OOGHqRm=2=pG>PCt+HWDi-e>{tKab)e55^(v{X|nWX0z@1ZiGo zVDi`7H>Cgi0(i6igj4e@Xt!RF7HRFQ{V9A)tH&7*t23|u$% zMAtPweo4B^3s0G>sX4-A8?17Fm^zd@Iar(iZY>1VmaJC)=tw>_NmU6cqIF}cgB5yp zR-FE%$3U5oEu+O%^c9jVLb67E1;opA@HL8F__)O;dPj#U@g)m{ zC+Is8*XJ z$)#nqdJFZk$ZpEi`zy~LgTJb^)}~a@jkDM;NORPB1<@eA?5&CAvRcEbm8EF01HC?N zVTQbQ#fEgj+b~z_z!4fPDZwEw5R!HH1{KPIFgVk@-huZt3ri?DT#WNrD2%8rC?^?c zO22*rax?>1$ZRs}Ds(^Akt3#d(;-qvwv+o5^IRw_`!U@Y&z%`1|Nr41^(}2v@Xwex zmld|bHHc;TBP|1cn+D@UL!B#zqwC`y(d$0;;A6+Wx9{i!Hy{1}!^iKw_l-k4k3WfBfF#kM2MEy+e)BYZ74<`~fj&U<#9dO5^i}648G5is8+% zc>l2blIUQ}Ju-iDcW*rAj`qjgEraoB-=+lp)wRui;i?w*ig^E~Xu{p#Ue!8pKK-+7 zV6cC~=B~!>fxgD!q0kbFd(N}BxLbW*RfR`0kf3_!@)Cc^ftSh3@A(NA&oVXP1;8Xr`DO}8nOX-MQS^#io zFNDC05m140-$J+$Jj%NZX&l+8NQep-y>k zF>eC{Y^?kRR`K$orRg)W8JvYM5eVJ{i7XSi>1k33m+K^mxc% zoE|Sxk{QD&BfXQ*x0)VbVc^ACJ`s;$mFhn$i1{m~+l^)Hhx&Kmr32#+LvC_L19gV;gsKcYDZjj0!)5A=gQKJ#U71HnYWSzT zy%AFB-9vDt>ZvUGVqmno4ozBx=j(xz=tIoXImwS5-$RVE}2*+!OZhhI)w_(o^fphcUI3}!5pWlP}h z^oeWftwhC`d{ImZ)!5e@L8?GA70Ki~m#t3!?5p%5qgq`qie}R)U77AS9XW2EW4_P) zzNO5v*m9%gKdtrFL)MpV1NMA--2QL;Dr)j4xc$>oGhfiui%tjQ=bU;WBWk8)(ihOw z<0%gFT)nZHaXNFe89zLdb;N&%AHIa8Zurc-eToO}neNepNF|-Qq3OPNHnaMywi8}g zS=>wWVWM(lFI>ubSF%zADEk(|GU`+7H^Oe}Qx9!~5@=9f-UtfRD+7J-eXec^E0uym z57?#L>~*Zv0!mvev{Md)XOm?#$+aDJQ6E~vCfh(gAEp}gjxN&%#kdn@F)7mq>K{WU zW&Jl`Cg+>Xb(NxjWTz#4kfcQCKc>L7MR0GoqpMunt5%St;aBfk;0%xJ_X7sQ9%ORtF!B8T1}nb@S0 ze#Am=HKuyR)ETCQit#WsGOJ7rDW>L{luKU$E9aviWh3$(OXeaY9-=3Z(&HR6g)dhb zbjsmsd^(cr2ICaAUFF~DCoCEpuTk?z(;KbRn0uAY2Rl;x0)KkiU(E@C0}}*>+^HD@ znnHlQiFAURT2S*0#ez!8>N|9HtMzM^eDbd(Wa7Cu4c9YA^c@Gr=jlh1*M)O*KzXwr z7+%-nS6lisheqePlhphAX`j8~?00_G$JbPQhBs{P>*|jtHV%2b5sxoUr7!O7ii%wU zZ#)=^L}NW+e>fcN>W&4veZHn1<-k#>x|m*`%j?Tyn+K!AiGlud%78{1!`=w}MxefY zOCmPBfffY<_2nDliS-+X%bO!1Z+&@JVwl>;X;moPLtXxW=nICs!cCzjUysipP-i=Dcxd`$H;Ik3<_)Me27ICzhy*@$CX*dO+=))1 z^RrCkXZ9iL98Iyl>~!=@mYspt?pvo$KtS}weV%|A_r)STzF5>JM!LHKA{|gqFc$ZP zx?+*0VCeMY7aRSwhK`@}2fTh?NO4e({7gG3l&lW$I%pSakg-)ii~N*>IL#@lL!4HU zyXwQKgNS-Pv4AJ)jRhjn5wRy6kI+@~2E2i$SR~XG4ut8T0^vxf-{)=I)W3d5dG|mp z-d)~Y-nS{fK0p_>d!TFUhiLLd1%gvgl-T6Y7egVj zzC5})mKbQsbmLb}ir_l)+*JDM+^iO=KKtBg@Gxa+v;rgl#?+Y!cYA_yCxHnu38?cX!0^3rBr{NSE^3Z^4=PER*4H3mA diff --git a/src/app/api/files/content/route.ts b/src/app/api/files/content/route.ts index a010e92..b27fafb 100644 --- a/src/app/api/files/content/route.ts +++ b/src/app/api/files/content/route.ts @@ -13,20 +13,23 @@ export async function GET(request: Request) { return NextResponse.json({ error: "Path is required" }, { status: 400 }); } + // Decode the URL-encoded path to handle special characters + const decodedFilePath = decodeURIComponent(filePath); + try { // Validate file exists - if (!fs.existsSync(filePath)) { + if (!fs.existsSync(decodedFilePath)) { return NextResponse.json({ error: "File not found" }, { status: 404 }); } // Check if it's a file (not directory) - const stats = fs.statSync(filePath); + const stats = fs.statSync(decodedFilePath); if (!stats.isFile()) { return NextResponse.json({ error: "Path is not a file" }, { status: 400 }); } // Check if it's a text file - const ext = path.extname(filePath).toLowerCase().replace('.', ''); + const ext = path.extname(decodedFilePath).toLowerCase().replace('.', ''); if (!TEXT_EXTENSIONS.includes(ext)) { return NextResponse.json({ error: "File type not supported" }, { status: 400 }); } @@ -41,10 +44,10 @@ export async function GET(request: Request) { let content = ''; try { if (encoding === 'utf8') { - content = fs.readFileSync(filePath, 'utf-8'); + content = fs.readFileSync(decodedFilePath, 'utf-8'); } else { // For non-UTF-8 encodings, read as buffer and convert - const buffer = fs.readFileSync(filePath); + const buffer = fs.readFileSync(decodedFilePath); const iconv = require('iconv-lite'); content = iconv.decode(buffer, encoding); } @@ -55,9 +58,9 @@ export async function GET(request: Request) { for (const fallbackEncoding of fallbackEncodings) { try { if (fallbackEncoding === 'utf8') { - content = fs.readFileSync(filePath, 'utf-8'); + content = fs.readFileSync(decodedFilePath, 'utf-8'); } else { - const buffer = fs.readFileSync(filePath); + const buffer = fs.readFileSync(decodedFilePath); const iconv = require('iconv-lite'); content = iconv.decode(buffer, fallbackEncoding); } @@ -74,13 +77,13 @@ export async function GET(request: Request) { // If no encoding worked, try reading as buffer and return as base64 if (!content || content.length === 0) { try { - const buffer = fs.readFileSync(filePath); + const buffer = fs.readFileSync(decodedFilePath); content = buffer.toString('base64'); return NextResponse.json({ content, size: stats.size, - path: filePath, - name: path.basename(filePath), + path: decodedFilePath, + name: path.basename(decodedFilePath), encoding: 'base64' }); } catch (err) { @@ -91,8 +94,8 @@ export async function GET(request: Request) { return NextResponse.json({ content, size: stats.size, - path: filePath, - name: path.basename(filePath), + path: decodedFilePath, + name: path.basename(decodedFilePath), encoding: encoding }); } catch (error: any) { diff --git a/src/app/api/files/route.ts b/src/app/api/files/route.ts index 0ed1f5b..5a5c83f 100644 --- a/src/app/api/files/route.ts +++ b/src/app/api/files/route.ts @@ -16,19 +16,22 @@ export async function GET(request: Request) { return NextResponse.json({ error: "Path is required" }, { status: 400 }); } + // Decode the URL-encoded path to handle special characters like +, spaces, Chinese characters, etc. + const decodedPath = decodeURIComponent(dirPath); + try { const db = getDatabase(); - const files = fs.readdirSync(dirPath); + const files = fs.readdirSync(decodedPath); // Get media files from database for this path const mediaFiles = db.prepare(` SELECT id, path, type, thumbnail, avg_rating, star_count FROM media WHERE path LIKE ? - `).all(`${dirPath}%`) as Array<{id: number, path: string, type: string, thumbnail: string | null, avg_rating: number, star_count: number}>; + `).all(`${decodedPath}%`) as Array<{id: number, path: string, type: string, thumbnail: string | null, avg_rating: number, star_count: number}>; const result = files.map((file) => { - const filePath = path.join(dirPath, file); + const filePath = path.join(decodedPath, file); const stats = fs.statSync(filePath); const ext = path.extname(file).toLowerCase();