Initial profile site commit

This commit is contained in:
2026-05-31 21:05:59 +09:00
commit db81f0e4a4
49 changed files with 18829 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
<?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);