222 lines
7.2 KiB
PHP
222 lines
7.2 KiB
PHP
|
|
<?php
|
||
|
|
require_once 'config.php';
|
||
|
|
require_once __DIR__ . '/error_config.php';
|
||
|
|
session_start();
|
||
|
|
set_json_headers();
|
||
|
|
|
||
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
||
|
|
|
||
|
|
// =====================================================
|
||
|
|
// 헬퍼: images 배열 정규화
|
||
|
|
// =====================================================
|
||
|
|
function normalize_images($input) {
|
||
|
|
if (isset($input['images']) && is_array($input['images'])) {
|
||
|
|
$images = array_values(array_filter(
|
||
|
|
array_map('trim', $input['images']),
|
||
|
|
fn($v) => $v !== ''
|
||
|
|
));
|
||
|
|
return $images;
|
||
|
|
}
|
||
|
|
if (isset($input['image']) && trim($input['image']) !== '') {
|
||
|
|
return [trim($input['image'])];
|
||
|
|
}
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalize_stack($input) {
|
||
|
|
if (isset($input['stack']) && is_array($input['stack'])) {
|
||
|
|
return array_values(array_filter(
|
||
|
|
array_map('trim', $input['stack']),
|
||
|
|
fn($v) => $v !== ''
|
||
|
|
));
|
||
|
|
}
|
||
|
|
if (isset($input['stack']) && is_string($input['stack'])) {
|
||
|
|
return array_values(array_filter(
|
||
|
|
array_map('trim', explode(',', $input['stack'])),
|
||
|
|
fn($v) => $v !== ''
|
||
|
|
));
|
||
|
|
}
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
// 기존 프로젝트 데이터를 새 형식으로 마이그레이션
|
||
|
|
function migrate_project_data($project) {
|
||
|
|
if (!isset($project['images']) || !is_array($project['images'])) {
|
||
|
|
$project['images'] = [];
|
||
|
|
if (!empty($project['image'])) {
|
||
|
|
$project['images'][] = $project['image'];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$project['image'] = $project['images'][0] ?? '';
|
||
|
|
|
||
|
|
// 새 필드들 기본값
|
||
|
|
if (!isset($project['stack']) || !is_array($project['stack'])) $project['stack'] = [];
|
||
|
|
if (!isset($project['period_start'])) $project['period_start'] = '';
|
||
|
|
if (!isset($project['period_end'])) $project['period_end'] = '';
|
||
|
|
if (!isset($project['video_url'])) $project['video_url'] = '';
|
||
|
|
|
||
|
|
// 기존 demo_url 필드는 제거 (마이그레이션)
|
||
|
|
unset($project['demo_url']);
|
||
|
|
|
||
|
|
return $project;
|
||
|
|
}
|
||
|
|
|
||
|
|
// =====================================================
|
||
|
|
// GET: 프로젝트 목록 조회
|
||
|
|
// =====================================================
|
||
|
|
if ($method === 'GET') {
|
||
|
|
$projects = read_json_safe(PROJECTS_FILE);
|
||
|
|
if ($projects === null) {
|
||
|
|
json_response(['error' => '데이터를 읽을 수 없습니다'], 500);
|
||
|
|
}
|
||
|
|
|
||
|
|
$projects = array_map('migrate_project_data', $projects);
|
||
|
|
|
||
|
|
usort($projects, function($a, $b) {
|
||
|
|
return ($b['id'] ?? 0) - ($a['id'] ?? 0);
|
||
|
|
});
|
||
|
|
|
||
|
|
json_response($projects);
|
||
|
|
}
|
||
|
|
|
||
|
|
require_auth();
|
||
|
|
|
||
|
|
// =====================================================
|
||
|
|
// POST: 새 프로젝트 추가
|
||
|
|
// =====================================================
|
||
|
|
if ($method === 'POST') {
|
||
|
|
$input = get_json_input();
|
||
|
|
|
||
|
|
$title = trim($input['title'] ?? '');
|
||
|
|
$label = trim($input['label'] ?? '');
|
||
|
|
$description = trim($input['description'] ?? '');
|
||
|
|
|
||
|
|
if (empty($title) || empty($label) || empty($description)) {
|
||
|
|
json_response(['error' => '제목, 라벨, 설명은 필수입니다'], 400);
|
||
|
|
}
|
||
|
|
|
||
|
|
$projects = read_json_safe(PROJECTS_FILE) ?? [];
|
||
|
|
|
||
|
|
$maxId = 0;
|
||
|
|
foreach ($projects as $p) {
|
||
|
|
if (($p['id'] ?? 0) > $maxId) $maxId = $p['id'];
|
||
|
|
}
|
||
|
|
|
||
|
|
$images = normalize_images($input);
|
||
|
|
$stack = normalize_stack($input);
|
||
|
|
|
||
|
|
$newProject = [
|
||
|
|
'id' => $maxId + 1,
|
||
|
|
'title' => $title,
|
||
|
|
'label' => $label,
|
||
|
|
'description' => $description,
|
||
|
|
'icon' => trim($input['icon'] ?? 'fa-solid fa-code'),
|
||
|
|
'images' => $images,
|
||
|
|
'image' => $images[0] ?? '',
|
||
|
|
'link' => trim($input['link'] ?? ''),
|
||
|
|
'stack' => $stack,
|
||
|
|
'period_start' => trim($input['period_start'] ?? ''),
|
||
|
|
'period_end' => trim($input['period_end'] ?? ''),
|
||
|
|
'video_url' => trim($input['video_url'] ?? ''),
|
||
|
|
'created_at' => date('Y-m-d')
|
||
|
|
];
|
||
|
|
|
||
|
|
$projects[] = $newProject;
|
||
|
|
|
||
|
|
if (write_json_safe(PROJECTS_FILE, $projects)) {
|
||
|
|
json_response(['success' => true, 'project' => $newProject]);
|
||
|
|
} else {
|
||
|
|
json_response(['error' => '저장에 실패했습니다'], 500);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// =====================================================
|
||
|
|
// PUT: 프로젝트 수정
|
||
|
|
// =====================================================
|
||
|
|
if ($method === 'PUT') {
|
||
|
|
$input = get_json_input();
|
||
|
|
$id = intval($input['id'] ?? 0);
|
||
|
|
|
||
|
|
if ($id <= 0) {
|
||
|
|
json_response(['error' => '유효하지 않은 ID'], 400);
|
||
|
|
}
|
||
|
|
|
||
|
|
$projects = read_json_safe(PROJECTS_FILE) ?? [];
|
||
|
|
$found = false;
|
||
|
|
|
||
|
|
foreach ($projects as $key => $project) {
|
||
|
|
if ($project['id'] === $id) {
|
||
|
|
$projects[$key]['title'] = trim($input['title'] ?? $project['title']);
|
||
|
|
$projects[$key]['label'] = trim($input['label'] ?? $project['label']);
|
||
|
|
$projects[$key]['description'] = trim($input['description'] ?? $project['description']);
|
||
|
|
$projects[$key]['icon'] = trim($input['icon'] ?? $project['icon']);
|
||
|
|
$projects[$key]['link'] = trim($input['link'] ?? $project['link']);
|
||
|
|
|
||
|
|
if (isset($input['images']) || isset($input['image'])) {
|
||
|
|
$images = normalize_images($input);
|
||
|
|
$projects[$key]['images'] = $images;
|
||
|
|
$projects[$key]['image'] = $images[0] ?? '';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isset($input['stack'])) {
|
||
|
|
$projects[$key]['stack'] = normalize_stack($input);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isset($input['period_start'])) {
|
||
|
|
$projects[$key]['period_start'] = trim($input['period_start']);
|
||
|
|
}
|
||
|
|
if (isset($input['period_end'])) {
|
||
|
|
$projects[$key]['period_end'] = trim($input['period_end']);
|
||
|
|
}
|
||
|
|
if (isset($input['video_url'])) {
|
||
|
|
$projects[$key]['video_url'] = trim($input['video_url']);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 기존 demo_url 필드 제거
|
||
|
|
unset($projects[$key]['demo_url']);
|
||
|
|
|
||
|
|
$projects[$key]['updated_at'] = date('Y-m-d');
|
||
|
|
$found = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!$found) {
|
||
|
|
json_response(['error' => '프로젝트를 찾을 수 없습니다'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (write_json_safe(PROJECTS_FILE, $projects)) {
|
||
|
|
json_response(['success' => true]);
|
||
|
|
} else {
|
||
|
|
json_response(['error' => '저장에 실패했습니다'], 500);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// =====================================================
|
||
|
|
// DELETE: 프로젝트 삭제
|
||
|
|
// =====================================================
|
||
|
|
if ($method === 'DELETE') {
|
||
|
|
$id = intval($_GET['id'] ?? 0);
|
||
|
|
|
||
|
|
if ($id <= 0) {
|
||
|
|
json_response(['error' => '유효하지 않은 ID'], 400);
|
||
|
|
}
|
||
|
|
|
||
|
|
$projects = read_json_safe(PROJECTS_FILE) ?? [];
|
||
|
|
$filtered = array_values(array_filter($projects, function($p) use ($id) {
|
||
|
|
return ($p['id'] ?? 0) !== $id;
|
||
|
|
}));
|
||
|
|
|
||
|
|
if (count($filtered) === count($projects)) {
|
||
|
|
json_response(['error' => '프로젝트를 찾을 수 없습니다'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (write_json_safe(PROJECTS_FILE, $filtered)) {
|
||
|
|
json_response(['success' => true]);
|
||
|
|
} else {
|
||
|
|
json_response(['error' => '삭제에 실패했습니다'], 500);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
json_response(['error' => 'Method not allowed'], 405);
|