최초 커밋
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>배포 파일 관리 시스템</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
</head>
|
||||
<body class="bg-gray-100 p-6">
|
||||
<div class="max-w-screen-xl mx-auto">
|
||||
<div class="flex items-center mb-4">
|
||||
<svg class="w-7 h-7 text-blue-600 mr-2" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5-5m0 0l5 5m-5-5v12" />
|
||||
</svg>
|
||||
<span class="text-xl font-bold mr-4">파일 업로드 관리자</span>
|
||||
</div>
|
||||
|
||||
<!-- 헤드 구성 -->
|
||||
<div class="bg-white p-4 rounded shadow mb-6 flex items-center justify-between">
|
||||
<div class="flex items-center space-x-4">
|
||||
<label for="product-select" class="font-bold">제품명(제품코드):</label>
|
||||
<select id="product-select" class="p-2 border border-gray-300 rounded">
|
||||
<option value="1">EG-BIM</option>
|
||||
<option value="2">TOVA</option>
|
||||
<option value="3">GAIA</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div>
|
||||
<span class="text-sm">내부 배포 버전(최신):</span>
|
||||
<span class="text-blue-600 font-semibold">0.28.1</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<span class="text-sm">외부 배포 버전:</span>
|
||||
<input type="number" id="ver1" class="w-14 p-1 border rounded" min="0" max="99">
|
||||
<span class="text-sm">.</span>
|
||||
<input type="number" id="ver2" class="w-14 p-1 border rounded" min="0" max="99">
|
||||
<span class="text-sm">.</span>
|
||||
<input type="number" id="ver3" class="w-14 p-1 border rounded" min="0" max="99">
|
||||
</div>
|
||||
<button id="save-version" class="bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600">저장</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 버튼 영역 -->
|
||||
<div class="flex justify-end space-x-2 mb-4">
|
||||
<button id="btn-new" class="bg-green-500 text-white px-3 py-1 rounded hover:bg-green-600">+ 신규 버전</button>
|
||||
<button class="bg-gray-500 text-white px-3 py-1 rounded hover:bg-gray-600">다운로드</button>
|
||||
<button class="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600">파일삭제</button>
|
||||
</div>
|
||||
|
||||
<!-- 테이블 -->
|
||||
<div class="bg-white shadow rounded-lg overflow-x-auto">
|
||||
<table class="min-w-full text-sm text-left">
|
||||
<thead class="bg-gray-200">
|
||||
<tr>
|
||||
<th class="px-4 py-2"><input type="checkbox" id="select-all" /></th>
|
||||
<th class="px-4 py-2">번호</th>
|
||||
<th class="px-4 py-2">파일명</th>
|
||||
<th class="px-4 py-2">파일타입</th>
|
||||
<th class="px-4 py-2">버전</th>
|
||||
<th class="px-4 py-2">크기</th>
|
||||
<th class="px-4 py-2">업로드일</th>
|
||||
<th class="px-4 py-2">업로드자</th>
|
||||
<th class="px-4 py-2">테스트완료</th>
|
||||
<th class="px-4 py-2">테스트자</th>
|
||||
<th class="px-4 py-2">릴리즈일</th>
|
||||
<th class="px-4 py-2">릴리즈자</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="file-list">
|
||||
<!-- JavaScript로 동적 삽입 예정 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 신규 버전 업로드 팝업(모달) -->
|
||||
<div id="upload-modal" class="fixed inset-0 bg-black bg-opacity-40 flex items-center justify-center z-50 hidden">
|
||||
<div class="bg-white p-8 rounded shadow-lg w-full max-w-lg relative">
|
||||
<h2 class="text-xl font-bold mb-6 text-center">
|
||||
신규 버전 업로드
|
||||
<span class="text-base text-gray-500">(테스트 서버)</span>
|
||||
</h2>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center">
|
||||
<label class="w-28">S/W 버전 :</label>
|
||||
<input type="number" class="border w-14 p-1 mx-1 text-center" id="sw1" min="0">
|
||||
<span>.</span>
|
||||
<input type="number" class="border w-14 p-1 mx-1 text-center" id="sw2" min="0">
|
||||
<span>.</span>
|
||||
<input type="number" class="border w-14 p-1 mx-1 text-center" id="sw3" min="0">
|
||||
<span class="ml-3 text-gray-500">MAJOR</span>
|
||||
</div>
|
||||
|
||||
<!-- 설치파일 -->
|
||||
<div class="flex items-center mb-2">
|
||||
<label class="w-39 flex justify-between items-center">
|
||||
<span>설치파일(EXE)</span>
|
||||
<span>:</span>
|
||||
</label>
|
||||
<input type="file" class="hidden" id="setup-file" accept=".exe">
|
||||
<input type="text" class="border flex-1 p-1 bg-gray-100 ml-2" id="setup-filename" readonly>
|
||||
<button type="button" class="ml-2 px-3 py-1 bg-blue-500 text-white rounded" id="setup-add">추가</button>
|
||||
</div>
|
||||
|
||||
<!-- 패치파일 -->
|
||||
<div class="flex items-center mb-2">
|
||||
<label class="w-39 flex justify-between items-center">
|
||||
<span>패치파일(EXE)</span>
|
||||
<span>:</span>
|
||||
</label>
|
||||
<input type="file" class="hidden" id="patch-file" accept=".exe">
|
||||
<input type="text" class="border flex-1 p-1 bg-gray-100 ml-2" id="patch-filename" readonly>
|
||||
<button type="button" class="ml-2 px-3 py-1 bg-blue-500 text-white rounded" id="patch-add">추가</button>
|
||||
</div>
|
||||
|
||||
<!-- 정보파일 -->
|
||||
<div class="flex items-center mb-2">
|
||||
<label class="w-39 flex justify-between items-center">
|
||||
<span class="flex items-center">
|
||||
정보파일
|
||||
<span class="relative group cursor-pointer ml-1">
|
||||
<span class="w-5 h-5 flex items-center justify-center bg-blue-100 rounded-full text-blue-700 font-bold">i</span>
|
||||
<span class="absolute left-1/2 -translate-x-1/2 top-7 z-50 hidden group-hover:block bg-gray-800 text-white text-xs rounded py-1 px-2 whitespace-nowrap shadow-lg">
|
||||
2개 이상의 파일을 업로드 시 압축파일 형태로 업로드해주세요
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span>:</span>
|
||||
</label>
|
||||
<input type="file" class="hidden" id="info-file" accept=".zip">
|
||||
<input type="text" class="border flex-1 p-1 bg-gray-100 ml-2" id="info-filename" readonly>
|
||||
<button type="button" class="ml-2 px-3 py-1 bg-blue-500 text-white rounded" id="info-add">추가</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$('#save-version').on('click', function() {
|
||||
const product = $('#product-select').val().replace(/['"]/g, '');
|
||||
const family = $('.text-blue-600').text().replace(/['"]/g, '');
|
||||
const v1 = $('#ver1').val();
|
||||
const v2 = $('#ver2').val();
|
||||
const v3 = $('#ver3').val();
|
||||
|
||||
if(!v1 || !v2 || !v3) {
|
||||
alert("외부 버전 모든 칸을 입력하세요");
|
||||
return;
|
||||
}
|
||||
|
||||
const external = [v1, v2, v3].join('.');
|
||||
|
||||
// 실제 전송 (주석처리 상태)
|
||||
// $.post('/egbim1/save_version.php', {
|
||||
// product_code: product,
|
||||
// family_version: family,
|
||||
// external_version: external
|
||||
// }, function(res) {
|
||||
// alert(res.message || '저장 완료');
|
||||
// }, 'json');
|
||||
});
|
||||
|
||||
// 전체 선택 체크박스 처리
|
||||
$('#select-all').on('change', function() {
|
||||
$('.file-checkbox').prop('checked', this.checked);
|
||||
});
|
||||
|
||||
// 신규버전 버튼 → 팝업
|
||||
$('#btn-new').on('click', function() {
|
||||
$('#upload-modal').removeClass('hidden');
|
||||
});
|
||||
|
||||
// 취소 버튼, 바깥 클릭 → 닫기
|
||||
$('#upload-cancel').on('click', function() {
|
||||
$('#upload-modal').addClass('hidden');
|
||||
});
|
||||
$('#upload-modal').on('click', function(e){
|
||||
if (e.target === this) $(this).addClass('hidden');
|
||||
});
|
||||
|
||||
// 파일 추가 버튼
|
||||
$('#setup-add').on('click', function() { $('#setup-file').click(); });
|
||||
$('#patch-add').on('click', function() { $('#patch-file').click(); });
|
||||
$('#info-add').on('click', function() { $('#info-file').click(); });
|
||||
|
||||
// 파일명 표시
|
||||
$('#setup-file').on('change', function(){ $('#setup-filename').val(this.files[0]?.name || ''); });
|
||||
$('#patch-file').on('change', function(){ $('#patch-filename').val(this.files[0]?.name || ''); });
|
||||
$('#info-file').on('change', function(){ $('#info-filename').val(this.files[0]?.name || ''); });
|
||||
|
||||
//cloudflare 업로드
|
||||
async function uploadToR2(file) {
|
||||
// 1) 서버에서 presigned URL 요청
|
||||
let formData = new FormData();
|
||||
formData.append("filename", file.name);
|
||||
|
||||
let res = await fetch("/egbim1/upload_presign.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
|
||||
let data = await res.json();
|
||||
console.log("Presigned URL:", data.url);
|
||||
|
||||
// 2) presigned URL로 직접 업로드
|
||||
let putRes = await fetch(data.url, {
|
||||
method: "PUT",
|
||||
body: file
|
||||
});
|
||||
|
||||
if (putRes.ok) {
|
||||
alert(file.name + " 업로드 성공!");
|
||||
|
||||
// 👉 DB 저장 호출 (버전 + 파일 정보)
|
||||
$.post('/egbim1/save_version.php', {
|
||||
filename: file.name,
|
||||
product_code: $('#product-select').val(),
|
||||
version: [$('#sw1').val(), $('#sw2').val(), $('#sw3').val()].join('.')
|
||||
}, function(res) {
|
||||
console.log(res);
|
||||
}, 'json');
|
||||
|
||||
} else {
|
||||
alert(file.name + " 업로드 실패!");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// DB 연결 예시
|
||||
$conn = new mysqli('localhost', 'egbim', 'baron3840!!', 'egbim');
|
||||
if ($conn->connect_error) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['message' => 'DB 연결 실패']);
|
||||
exit;
|
||||
}
|
||||
mysqli_set_charset($conn, 'utf8mb4');
|
||||
|
||||
// 파라미터 받기
|
||||
$product_code = $_POST['product_code'] ?? '';
|
||||
$family_version = $_POST['family_version'] ?? '';
|
||||
$external_version = $_POST['external_version'] ?? '';
|
||||
|
||||
// 필수 값 체크
|
||||
if (!$product_code || !$family_version || !$external_version) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['message' => '필수값 누락']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// INSERT or UPDATE (upsert)
|
||||
$sql = "INSERT INTO deploy_versions (product_code, family_version, external_version)
|
||||
VALUES (?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
family_version = VALUES(family_version),
|
||||
external_version = VALUES(external_version)";
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->bind_param("sss", $product_code, $family_version, $external_version);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
echo json_encode(['message' => '저장 성공']);
|
||||
} else {
|
||||
http_response_code(500);
|
||||
echo json_encode(['message' => 'DB 오류: ' . $stmt->error]);
|
||||
}
|
||||
$stmt->close();
|
||||
$conn->close();
|
||||
?>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Aws\S3\S3Client;
|
||||
use Aws\Credentials\Credentials;
|
||||
|
||||
$accountId = "81fa2d48964d31dd0da9558f9ce601d1";
|
||||
$bucket = "baron-software-test";
|
||||
$accessKey = "9ade5915f7abd402db60a87f3b3b6d76"; // write 권한
|
||||
$secretKey = "a1a1cfd59f739977f98fb73f214894e39dbac9a39b1f6208020aa2e1303ea54c";
|
||||
|
||||
$credentials = new Credentials($accessKey, $secretKey);
|
||||
|
||||
$s3 = new S3Client([
|
||||
'region' => 'auto',
|
||||
'endpoint' => "https://{$accountId}.r2.cloudflarestorage.com",
|
||||
'version' => 'latest',
|
||||
'credentials' => $credentials,
|
||||
]);
|
||||
|
||||
$filename = $_POST['filename'] ?? ('temp-' . time());
|
||||
|
||||
// presigned URL 발급
|
||||
$cmd = $s3->getCommand('PutObject', [
|
||||
'Bucket' => $bucket,
|
||||
'Key' => "uploads/" . basename($filename)
|
||||
]);
|
||||
|
||||
$request = $s3->createPresignedRequest($cmd, '+1 hour');
|
||||
|
||||
echo json_encode([
|
||||
"url" => (string)$request->getUri(),
|
||||
"filename" => $filename
|
||||
]);
|
||||
Reference in New Issue
Block a user