fix: image upload proxy, compression, and JSON parse safety

- Add /api/v1/ and /files/ rewrite rules in next.config.ts so frontend
  can call backend without /admin139 prefix
- Fix upload using MultiPartParser instead of req->getUploadedFiles()
- Add client-side image compression (canvas resize to 1920px, quality 0.8)
  for photos >500KB before upload
- Safe JSON parsing: catch HTML error responses instead of throwing
  SyntaxError on non-JSON backend responses
- Fix backslash escape in delete filename validation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
这个提交包含在:
cryptocommuniums-afk
2026-02-16 18:10:47 +08:00
父节点 cfbe9a0363
当前提交 7dd10bef2d
修改 3 个文件,包含 190 行新增4 行删除

查看文件

@@ -12,7 +12,11 @@
#include "csp/services/learning_note_scoring_service.h"
#include "http_auth.h"
#include <drogon/MultiPart.h>
#include <algorithm>
#include <filesystem>
#include <memory>
#include <exception>
#include <optional>
#include <stdexcept>
@@ -363,7 +367,12 @@ void MeController::uploadWrongBookNoteImages(
const auto user_id = RequireAuth(req, cb);
if (!user_id.has_value()) return;
const auto files = req->getUploadedFiles();
drogon::MultiPartParser parser;
if (parser.parse(req) != 0) {
cb(JsonError(drogon::k400BadRequest, "bad multipart"));
return;
}
const auto& files = parser.getFiles();
if (files.empty()) {
cb(JsonError(drogon::k400BadRequest, "no files"));
return;
@@ -398,8 +407,12 @@ void MeController::uploadWrongBookNoteImages(
for (const auto &f : files) {
if ((int)arr.size() >= kMaxImages) break;
const auto ct_hdr = f.getFileType();
if (ct_hdr.rfind("image/", 0) != 0) {
// Allow common image extensions only (frontend also restricts accept=image/*)
std::string name_for_ext = f.getFileName();
auto dot = name_for_ext.find_last_of('.');
std::string ext_check = (dot == std::string::npos) ? std::string("") : name_for_ext.substr(dot);
for (auto &c : ext_check) c = (char)std::tolower((unsigned char)c);
if (!(ext_check==".png" || ext_check==".jpg" || ext_check==".jpeg" || ext_check==".gif" || ext_check==".webp")) {
continue;
}
if (f.fileLength() > 5 * 1024 * 1024) {
@@ -456,7 +469,7 @@ void MeController::deleteWrongBookNoteImage(
}
const std::string filename = (*json).get("filename", "").asString();
if (filename.empty() || filename.find("..") != std::string::npos || filename.find('/') != std::string::npos || filename.find('\\\\') != std::string::npos) {
if (filename.empty() || filename.find("..") != std::string::npos || filename.find('/') != std::string::npos || filename.find('\\') != std::string::npos) {
cb(JsonError(drogon::k400BadRequest, "bad filename"));
return;
}