리빌더 부분 추가
This commit is contained in:
2
plugin/editor/rb.editor/php/_common.php
Normal file
2
plugin/editor/rb.editor/php/_common.php
Normal file
@ -0,0 +1,2 @@
|
||||
<?php
|
||||
include_once("../../../../common.php");
|
||||
33
plugin/editor/rb.editor/php/rb.delete.php
Normal file
33
plugin/editor/rb.editor/php/rb.delete.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
include_once("./_common.php");
|
||||
|
||||
// 삭제 요청된 파일 URL 가져오기
|
||||
$file_url = isset($_POST['file']) ? $_POST['file'] : '';
|
||||
|
||||
if (!$file_url) {
|
||||
echo json_encode(["error" => "파일 경로가 제공되지 않았습니다."]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 업로드된 파일 경로에서 /data/editor/YYYY/MM/ 추출
|
||||
$pattern = "#/data/editor/(\d{4})/([\w\d_-]+\.\w+)$#"; // 2502/파일명.확장자 패턴
|
||||
if (!preg_match($pattern, $file_url, $matches)) {
|
||||
echo json_encode(["error" => "잘못된 파일 경로 형식입니다."]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$ym = $matches[1]; // 2502 (연월)
|
||||
$file_name = $matches[2]; // 1738844872_c13aee53489a52b7_...png
|
||||
|
||||
// 업로드된 디렉토리 경로
|
||||
$data_dir = G5_DATA_PATH . "/editor/" . $ym . "/";
|
||||
$file_path = $data_dir . $file_name;
|
||||
|
||||
// 파일 존재 여부 확인 후 삭제
|
||||
if (file_exists($file_path)) {
|
||||
unlink($file_path);
|
||||
echo json_encode(["success" => "파일이 삭제되었습니다.", "file" => $file_name]);
|
||||
} else {
|
||||
echo json_encode(["error" => "파일을 찾을 수 없습니다."]);
|
||||
}
|
||||
?>
|
||||
18
plugin/editor/rb.editor/php/rb.fonts.php
Normal file
18
plugin/editor/rb.editor/php/rb.fonts.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
include_once("./_common.php");
|
||||
$g5_editor = isset( $_GET['editor']) ? $_GET['editor'] : 'rb.editor';
|
||||
$editor_url = G5_PATH.'/plugin/editor/'.$g5_editor;
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// 대상 폴더 (fonts 경로)
|
||||
$folderPath = $editor_url.'/fonts';
|
||||
|
||||
if (is_dir($folderPath)) {
|
||||
$folders = array_filter(scandir($folderPath), function($item) use ($folderPath) {
|
||||
return $item !== '.' && $item !== '..' && is_dir($folderPath . DIRECTORY_SEPARATOR . $item);
|
||||
});
|
||||
echo json_encode(["folders" => array_values($folders)]);
|
||||
} else {
|
||||
echo json_encode(["error" => "폰트없음"]);
|
||||
}
|
||||
50
plugin/editor/rb.editor/php/rb.image_proxy.php
Normal file
50
plugin/editor/rb.editor/php/rb.image_proxy.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
// image_proxy.php
|
||||
if(isset($_GET['url'])) {
|
||||
$url = $_GET['url'];
|
||||
|
||||
// URL 유효성 검사
|
||||
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
http_response_code(400);
|
||||
exit('Invalid URL');
|
||||
}
|
||||
|
||||
// cURL 설정 및 실행
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 리디렉션 허용
|
||||
curl_setopt($ch, CURLOPT_MAXREDIRS, 5); // 최대 5번 리디렉션 허용
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36');
|
||||
curl_setopt($ch, CURLOPT_REFERER, $url); // 원본 URL을 Referer로 설정
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Accept: image/*', // 이미지 전용 Accept 헤더 추가
|
||||
'Cache-Control: no-cache', // 캐시 우회
|
||||
'Pragma: no-cache'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // SSL 검증 비활성화
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$data = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
$contentLength = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
|
||||
curl_close($ch);
|
||||
|
||||
// **HTTP 응답이 200이 아닌 경우에도 강제 출력**
|
||||
if ($httpCode === 200 && $data) {
|
||||
header("Content-Type: $contentType");
|
||||
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
||||
header("Pragma: no-cache");
|
||||
echo $data;
|
||||
} else {
|
||||
// **cURL이 실패하면 URL 직접 반환**
|
||||
header("Content-Type: text/plain");
|
||||
echo $url;
|
||||
}
|
||||
} else {
|
||||
http_response_code(400);
|
||||
exit('URL parameter is required');
|
||||
}
|
||||
?>
|
||||
143
plugin/editor/rb.editor/php/rb.metadata.php
Normal file
143
plugin/editor/rb.editor/php/rb.metadata.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header('Content-Type: application/json; charset=UTF-8');
|
||||
header('Access-Control-Allow-Credentials: true');
|
||||
header('Set-Cookie: my_cookie=value; SameSite=None; Secure');
|
||||
|
||||
function isFunctionAvailable($function) {
|
||||
return function_exists($function);
|
||||
}
|
||||
|
||||
function curl_get_contents($url) {
|
||||
if (!isFunctionAvailable('curl_init')) {
|
||||
return ['error' => 'CURL 기능이 서버에서 지원되지 않습니다.'];
|
||||
}
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36');
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
|
||||
'Referer: https://www.google.com/',
|
||||
'Upgrade-Insecure-Requests: 1'
|
||||
]);
|
||||
|
||||
$data = curl_exec($ch);
|
||||
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
$error_msg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
return ['error' => 'CURL 에러: ' . $error_msg];
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
return ['html' => $data, 'content_type' => $contentType];
|
||||
}
|
||||
|
||||
if (isset($_GET['url'])) {
|
||||
$url = $_GET['url'];
|
||||
|
||||
if (preg_match('/\.(jpg|jpeg|png|gif|webp|svg)$/i', $url)) {
|
||||
echo json_encode(['title' => 'Image', 'meta' => ['og:image' => $url]], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$result = curl_get_contents($url);
|
||||
if (isset($result['error'])) {
|
||||
echo json_encode(['error' => $result['error']], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$html = $result['html'];
|
||||
$contentType = $result['content_type'];
|
||||
|
||||
if (!$html) {
|
||||
echo json_encode(['error' => '데이터를 가져오지 못했습니다.'], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
preg_match('/<meta.*?charset=["\']?([^"\'>]+)/i', $html, $matches);
|
||||
$htmlCharset = isset($matches[1]) ? strtolower($matches[1]) : 'utf-8';
|
||||
|
||||
if (isFunctionAvailable('mb_detect_encoding')) {
|
||||
$detectedEncoding = mb_detect_encoding($html, ['UTF-8', 'EUC-KR', 'SJIS', 'ISO-8859-1', 'CP949'], true);
|
||||
} else {
|
||||
$detectedEncoding = false;
|
||||
}
|
||||
|
||||
if (isFunctionAvailable('mb_convert_encoding')) {
|
||||
if ($detectedEncoding && $detectedEncoding !== 'UTF-8') {
|
||||
$html = mb_convert_encoding($html, 'UTF-8', $detectedEncoding);
|
||||
} elseif ($htmlCharset !== 'utf-8' && $htmlCharset !== '') {
|
||||
$html = mb_convert_encoding($html, 'UTF-8', strtoupper($htmlCharset));
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('DOMDocument')) {
|
||||
echo json_encode(['error' => 'DOMDocument 확장이 필요합니다.'], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$dom = new DOMDocument();
|
||||
@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
|
||||
$metas = $dom->getElementsByTagName('meta');
|
||||
$metaData = [];
|
||||
|
||||
foreach ($metas as $meta) {
|
||||
$name = $meta->getAttribute('name');
|
||||
if (!$name) {
|
||||
$name = $meta->getAttribute('property');
|
||||
}
|
||||
$content = $meta->getAttribute('content');
|
||||
if ($name && $content) {
|
||||
$metaData[$name] = $content;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ og:image가 없으면 첫 번째 <img> 태그의 src를 가져옴
|
||||
if (!isset($metaData['og:image'])) {
|
||||
$imgTags = $dom->getElementsByTagName('img');
|
||||
$firstImage = null;
|
||||
|
||||
foreach ($imgTags as $img) {
|
||||
$src = $img->getAttribute('src');
|
||||
if (!$src) continue;
|
||||
|
||||
if (!preg_match('/^https?:\/\//', $src)) {
|
||||
$parsedUrl = parse_url($url);
|
||||
$base = $parsedUrl['scheme'] . '://' . $parsedUrl['host'];
|
||||
if (isset($parsedUrl['port'])) {
|
||||
$base .= ':' . $parsedUrl['port'];
|
||||
}
|
||||
$src = $base . (substr($src, 0, 1) === '/' ? '' : '/') . $src;
|
||||
}
|
||||
|
||||
$firstImage = $src;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($firstImage) {
|
||||
$metaData['og:image'] = $firstImage;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ <title> 태그 가져오기
|
||||
$titleElement = $dom->getElementsByTagName('title')->item(0);
|
||||
$title = $titleElement ? trim($titleElement->nodeValue) : '';
|
||||
|
||||
$response = [
|
||||
'title' => $title,
|
||||
'meta' => $metaData
|
||||
];
|
||||
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
} else {
|
||||
echo json_encode(['error' => 'URL 파라미터가 필요합니다.'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
?>
|
||||
20
plugin/editor/rb.editor/php/rb.metadata_proxy.php
Normal file
20
plugin/editor/rb.editor/php/rb.metadata_proxy.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
include_once("./_common.php");
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$url = $data['url'];
|
||||
|
||||
if (!$url) {
|
||||
echo json_encode(['error' => 'Invalid URL']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$editor_url = G5_EDITOR_URL.'/'.$config['cf_editor'];
|
||||
|
||||
// 🛠️ 실제 메타데이터 스크래핑을 수행하는 파일 호출
|
||||
$metadata_url = $editor_url.'/php/rb.metadata.php?url=' . urlencode($url);
|
||||
|
||||
$response = file_get_contents($metadata_url);
|
||||
echo $response;
|
||||
?>
|
||||
54
plugin/editor/rb.editor/php/rb.paste_image_proxy.php
Normal file
54
plugin/editor/rb.editor/php/rb.paste_image_proxy.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// CORS 허용
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
|
||||
// 요청된 이미지 URL 가져오기
|
||||
if (!isset($_GET['url'])) {
|
||||
http_response_code(400);
|
||||
echo "URL이 잘못 되었습니다.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$image_url = filter_var($_GET['url'], FILTER_SANITIZE_URL);
|
||||
|
||||
// URL 유효성 검사
|
||||
if (!filter_var($image_url, FILTER_VALIDATE_URL)) {
|
||||
http_response_code(400);
|
||||
echo "URL이 잘못 되었습니다.";
|
||||
exit;
|
||||
}
|
||||
|
||||
// CURL을 사용하여 이미지 다운로드
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $image_url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
|
||||
$image_data = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$mime_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($http_code !== 200 || !$image_data) {
|
||||
http_response_code(500);
|
||||
echo "이미지 파일 저장에 실패 하였습니다.";
|
||||
exit;
|
||||
}
|
||||
|
||||
// 이미지 타입 허용 (JPG, PNG, WEBP, GIF만 허용)
|
||||
$allowed_types = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
|
||||
if (!in_array($mime_type, $allowed_types)) {
|
||||
http_response_code(400);
|
||||
echo "이미지 파일만 가능합니다.";
|
||||
exit;
|
||||
}
|
||||
|
||||
// 올바른 Content-Type 설정 후 출력
|
||||
header("Content-Type: " . $mime_type);
|
||||
echo $image_data;
|
||||
?>
|
||||
30
plugin/editor/rb.editor/php/rb.sticker.php
Normal file
30
plugin/editor/rb.editor/php/rb.sticker.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
include_once("./_common.php");
|
||||
$g5_editor = isset( $_GET['editor']) ? $_GET['editor'] : 'rb.editor';
|
||||
$baseDir = G5_PATH.'/plugin/editor/'.$g5_editor.'/image/sticker/';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (isset($_GET['folder'])) {
|
||||
// 폴더의 파일 조회 (이미지 파일만)
|
||||
$folder = preg_replace('/[^a-zA-Z0-9-_ ]/', '', $_GET['folder']); // 보안 필터링
|
||||
$path = $baseDir . $folder;
|
||||
|
||||
if (is_dir($path)) {
|
||||
$files = array_values(array_filter(scandir($path), function ($file) use ($path) {
|
||||
return preg_match('/\.(png|jpe?g|gif|svg|webp)$/i', $file) && is_file("$path/$file");
|
||||
}));
|
||||
|
||||
echo json_encode(["files" => $files]);
|
||||
} else {
|
||||
echo json_encode(["error" => "폴더가 존재하지 않습니다."]);
|
||||
}
|
||||
} else {
|
||||
// 전체 폴더 목록 조회
|
||||
$folders = array_filter(glob($baseDir . '*'), 'is_dir');
|
||||
$folderNames = array_map('basename', $folders);
|
||||
|
||||
echo json_encode(["folders" => $folderNames]);
|
||||
}
|
||||
|
||||
?>
|
||||
83
plugin/editor/rb.editor/php/rb.upload.php
Normal file
83
plugin/editor/rb.editor/php/rb.upload.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
include_once("./_common.php");
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Nonce 관련 상수 정의 추가
|
||||
if (!defined('FT_NONCE_UNIQUE_KEY'))
|
||||
define('FT_NONCE_UNIQUE_KEY', sha1($_SERVER['SERVER_SOFTWARE'] . G5_MYSQL_USER . session_id() . G5_TABLE_PREFIX));
|
||||
|
||||
if (!defined('FT_NONCE_SESSION_KEY'))
|
||||
define('FT_NONCE_SESSION_KEY', substr(md5(FT_NONCE_UNIQUE_KEY), 5));
|
||||
|
||||
if (!defined('FT_NONCE_DURATION'))
|
||||
define('FT_NONCE_DURATION', 60 * 60); // 1시간 유효
|
||||
|
||||
if (!defined('FT_NONCE_KEY'))
|
||||
define('FT_NONCE_KEY', '_nonce');
|
||||
|
||||
// 세션에서 Nonce 값을 가져옴
|
||||
$session_nonce = isset($_SESSION['token_' . FT_NONCE_SESSION_KEY]) ? $_SESSION['token_' . FT_NONCE_SESSION_KEY] : '';
|
||||
|
||||
// 업로드 시 전달된 Nonce 값
|
||||
$posted_nonce = isset($_POST['editor_nonce']) ? $_POST['editor_nonce'] : '';
|
||||
|
||||
// Nonce 검증 로그 추가
|
||||
error_log("업로드 요청 Nonce 값: " . $posted_nonce);
|
||||
error_log("서버 세션 Nonce 값: " . $session_nonce);
|
||||
|
||||
if (!$session_nonce || $posted_nonce !== $session_nonce) {
|
||||
error_log("Nonce 불일치: 업로드된 nonce = " . $posted_nonce . " | 세션 nonce = " . $session_nonce);
|
||||
echo json_encode(array("files" => array(array("error" => "잘못된 접근입니다. (Nonce 불일치)"))));
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
// 업로드 디렉토리 설정
|
||||
$ym = date('ym', G5_SERVER_TIME);
|
||||
$data_dir = G5_DATA_PATH . '/editor/' . $ym . '/';
|
||||
$data_url = G5_DATA_URL . '/editor/' . $ym . '/';
|
||||
|
||||
// 디렉토리가 없으면 생성
|
||||
if (!is_dir($data_dir)) {
|
||||
if (!@mkdir($data_dir, G5_DIR_PERMISSION, true)) {
|
||||
error_log("업로드 디렉토리 생성 실패: " . $data_dir);
|
||||
echo json_encode(array("files" => array(array("error" => "업로드 디렉토리 생성에 실패했습니다."))));
|
||||
exit;
|
||||
}
|
||||
@chmod($data_dir, G5_DIR_PERMISSION);
|
||||
}
|
||||
|
||||
// 파일 업로드 검증
|
||||
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||||
error_log("파일 업로드 실패 (파일 정보 없음 또는 오류 코드 존재)");
|
||||
echo json_encode(array("files" => array(array("error" => "파일 업로드에 실패했습니다."))));
|
||||
exit;
|
||||
}
|
||||
|
||||
// 파일 저장 처리
|
||||
$tempfile = $_FILES['file']['tmp_name'];
|
||||
$filename = time() . '_' . bin2hex(random_bytes(8)) . '_' . $_FILES['file']['name'];
|
||||
$savefile = $data_dir . $filename;
|
||||
|
||||
if (!file_exists($tempfile)) {
|
||||
error_log("임시 파일이 존재하지 않음: " . $tempfile);
|
||||
echo json_encode(array("files" => array(array("error" => "임시 파일이 존재하지 않습니다."))));
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!move_uploaded_file($tempfile, $savefile)) {
|
||||
error_log("파일 저장 실패: " . $savefile);
|
||||
echo json_encode(array("files" => array(array("error" => "파일 저장에 실패했습니다."))));
|
||||
exit;
|
||||
}
|
||||
|
||||
// 파일 권한 설정
|
||||
@chmod($savefile, G5_FILE_PERMISSION);
|
||||
|
||||
// 업로드 성공 응답
|
||||
$file_url = $data_url . $filename;
|
||||
echo json_encode(array('files' => array(array('name' => $filename, 'url' => $file_url))));
|
||||
?>
|
||||
Reference in New Issue
Block a user