get('logged_in')) { return $this->response->setStatusCode(401)->setJSON(['ok' => false, 'message' => '로그인이 필요합니다.']); } $content = trim((string) $this->request->getPost('content')); if ($content === '') { return $this->response->setJSON(['ok' => false, 'message' => '의견 내용을 입력해 주세요.']); } if (mb_strlen($content) > 1000) { $content = mb_substr($content, 0, 1000); } helper('admin'); $lgIdx = function_exists('admin_effective_lg_idx') ? admin_effective_lg_idx() : null; if (! $lgIdx) { $raw = session()->get('mb_lg_idx'); $lgIdx = ($raw !== null && $raw !== '') ? (int) $raw : null; } $model = model(FeedbackModel::class); $id = $model->insert([ 'fb_lg_idx' => $lgIdx, 'fb_mb_idx' => (int) session()->get('mb_idx'), 'fb_mb_name' => (string) (session()->get('mb_name') ?? ''), 'fb_mb_level' => (int) session()->get('mb_level'), 'fb_content' => $content, 'fb_page_url' => mb_substr((string) $this->request->getPost('page_url'), 0, 255), 'fb_page_title' => mb_substr((string) $this->request->getPost('page_title'), 0, 255), 'fb_user_agent' => mb_substr((string) $this->request->getHeaderLine('User-Agent'), 0, 255), 'fb_status' => 'new', ], true); if (! $id) { return $this->response->setJSON(['ok' => false, 'message' => '저장 중 오류가 발생했습니다.']); } $dir = WRITEPATH . 'uploads/feedback/'; if (! is_dir($dir)) { @mkdir($dir, 0775, true); } $fileModel = model(FeedbackFileModel::class); // 1) 화면 캡처(dataURL) — 실패해도 의견 본문은 이미 저장됨 $shot = (string) $this->request->getPost('screenshot'); if ($shot !== '' && preg_match('#^data:image/(png|jpeg);base64,#', $shot, $m) === 1) { $ext = $m[1] === 'png' ? 'png' : 'jpg'; $bin = base64_decode(substr($shot, strpos($shot, ',') + 1), true); if ($bin !== false && strlen($bin) <= 5 * 1024 * 1024) { $file = 'fb_' . (int) $id . '_cap_' . bin2hex(random_bytes(4)) . '.' . $ext; if (@file_put_contents($dir . $file, $bin) !== false) { $model->update($id, ['fb_screenshot' => $file]); // 목록 캡처여부 표시용 $fileModel->insert(['ff_fb_idx' => $id, 'ff_kind' => 'capture', 'ff_path' => $file, 'ff_mime' => 'image/' . ($ext === 'png' ? 'png' : 'jpeg')]); } } } // 2) 사용자가 첨부한 이미지(여러 장) $allowed = ['image/png' => 'png', 'image/jpeg' => 'jpg', 'image/gif' => 'gif', 'image/webp' => 'webp']; $count = 0; foreach ($this->request->getFileMultiple('files') ?? [] as $f) { if ($count >= 5 || $f === null || ! $f->isValid() || $f->hasMoved()) { continue; } $mime = (string) $f->getMimeType(); if (! isset($allowed[$mime]) || $f->getSize() > 5 * 1024 * 1024) { continue; } $file = 'fb_' . (int) $id . '_up_' . bin2hex(random_bytes(4)) . '.' . $allowed[$mime]; try { $f->move($dir, $file); $fileModel->insert(['ff_fb_idx' => $id, 'ff_kind' => 'upload', 'ff_path' => $file, 'ff_mime' => $mime]); $count++; } catch (\Throwable $e) { // 개별 파일 실패는 건너뜀 } } return $this->response->setJSON(['ok' => true]); } }