<?php
require_once 'config.php';
require_once __DIR__ . '/includes/auth_helper.php';
// ãã°ã€ã³å¿
é
requireLogin();
// GETãªã¯ãšã¹ãã®å Žåã¯ãã©ãŒã ã衚瀺
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$pageTitle = 'è£
眮æ
å ±ç®¡çã·ã¹ãã - CSVã¢ããããŒã';
$errorMessage = getErrorMessage();
$successMessage = getSuccessMessage();
// å
±éããããŒãèªã¿èŸŒã¿
require_once 'includes/header.php';
?>
<div class="main-content">
<div class="page-container">
<div class="page-header">
<h1 class="page-title">
<div class="page-title-icon black-svg">
<?php include 'svgs/upload.svg'; ?>
</div>
CSVãã¡ã€ã«ã¢ããããŒã
</h1>
</div>
<?php if ($errorMessage): ?>
<div class="alert alert-error">
<svg class="alert-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"/>
</svg>
<div>
<strong>ãšã©ãŒ:</strong> <?= h($errorMessage) ?>
</div>
</div>
<?php endif; ?>
<?php if ($successMessage): ?>
<div class="alert alert-success">
<svg class="alert-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z"/>
</svg>
<div>
<strong>æå:</strong> <?= h($successMessage) ?>
</div>
</div>
<?php endif; ?>
<div class="info-box">
<h3>
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"/>
</svg>
CSVãã¡ã€ã«åœ¢åŒã«ã€ããŠ
</h3>
<p>以äžã®åœ¢åŒã®CSVãã¡ã€ã«ãã¢ããããŒãããŠãã ããïŒ</p>
<div class="csv-format">
ãµãŒãã¹å,è£
眮皮å¥,è£
眮åç§°,ãŠãŒã¶å1,è£
眮IP,ãã¹ã¯ãŒã,ãã®ä»ã«ã©ã 1,ãã®ä»ã«ã©ã 2
ãµãŒãã¹A,è£
眮皮å¥A,souchimei,admin,198.1.1.1,admin123,å€1,å€2
ãµãŒãã¹A,è£
眮皮å¥A,souchimei2,admin,198.1.1.2,admin123,å€3,å€4
</div>
<ul>
<li><strong>å¿
é é
ç®:</strong> ãµãŒãã¹åãè£
眮皮å¥ãè£
眮åç§°ããŠãŒã¶å</li>
<li><strong>ä»»æé
ç®:</strong> è£
眮IPããã¹ã¯ãŒã</li>
<li><strong>äž»ããŒ:</strong> [ãµãŒãã¹å]_[è£
眮皮å¥]_[è£
眮åç§°]_[ãŠãŒã¶å]</li>
<li><strong>ãã®ä»ã®ã«ã©ã :</strong>
<ul>
<li>device_infoããŒãã«ã«ååšããã«ã©ã ã®å Žå â device_infoã«ç»é²</li>
<li>ååšããªãå Žå â [ãµãŒãã¹å]_[è£
眮皮å¥]ããŒãã«ã«ç»é²</li>
<li>ã«ã©ã ãååšããªãå Žåã¯èªåã§è¿œå ãããŸã</li>
</ul>
</li>
<li><strong>æåãšã³ã³ãŒãã£ã³ã°:</strong> UTF-8</li>
<li><strong>ãã¡ã€ã«ãµã€ãºå¶é:</strong> æå€§10MB</li>
</ul>
</div>
<form id="uploadForm" action="upload.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?= h(generateCsrfToken()) ?>">
<div class="form-group flex-col">
<label for="csvFile">CSVãã¡ã€ã«ãéžæ:</label>
<!-- ãã©ãã°&ãããããšãªã¢ -->
<div class="drag-drop-area" id="dragDropArea">
<div class="drag-drop-icon">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
</svg>
</div>
<div class="drag-drop-text">
CSVãã¡ã€ã«ãããã«ãã©ãã°&ãããã
</div>
<div class="drag-drop-subtext">
ãŸãã¯<strong>ã¯ãªãã¯</strong>ããŠãã¡ã€ã«ãéžæ
</div>
</div>
<!-- é ããããã¡ã€ã«å
¥å -->
<input type="file"
id="csvFile"
name="csv_file"
accept=".csv,text/csv,application/csv"
class="file-input-hidden">
<!-- éžæããããã¡ã€ã«æ
å ± -->
<div class="selected-file-info" id="selectedFileInfo">
<div class="file-details">
<div class="file-icon">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
</svg>
</div>
<div class="file-info-text">
<div class="file-name" id="fileName"></div>
<div class="file-size" id="fileSize"></div>
</div>
</div>
</div>
<div class="file-info" style="margin-top: 10px;">
â» CSVãã¡ã€ã«ïŒ.csvïŒã®ã¿ã¢ããããŒãå¯èœã§ãïŒæå€§10MBïŒ
</div>
</div>
<div class="upload-progress" id="uploadProgress">
<div class="progress-bar" id="progressBar"></div>
</div>
<div class="form-group">
<button type="submit" class="btn" id="uploadBtn">
CSVãã¡ã€ã«ãã¢ããããŒã
</button>
</div>
</form>
</div>
<script>
// ãã©ãã°&ããããæ©èœ
const dragDropArea = document.getElementById('dragDropArea');
const fileInput = document.getElementById('csvFile');
const selectedFileInfo = document.getElementById('selectedFileInfo');
const fileName = document.getElementById('fileName');
const fileSize = document.getElementById('fileSize');
// ãã©ãã°&ãããããšãªã¢ã®ã¯ãªãã¯ã§ãã¡ã€ã«éžæãã€ã¢ãã°ãéã
dragDropArea.addEventListener('click', function() {
fileInput.click();
});
// ãã©ãã°ãªãŒããŒïŒãããããèš±å¯ããããå¿
é ïŒ
dragDropArea.addEventListener('dragover', function(e) {
e.preventDefault();
e.stopPropagation();
e.dataTransfer.dropEffect = 'copy';
dragDropArea.classList.add('drag-over');
});
// ãã©ãã°ãšã³ã¿ãŒ
dragDropArea.addEventListener('dragenter', function(e) {
e.preventDefault();
e.stopPropagation();
dragDropArea.classList.add('drag-over');
});
// ãã©ãã°ãªãŒã
dragDropArea.addEventListener('dragleave', function(e) {
e.preventDefault();
e.stopPropagation();
// ãããããšãªã¢èªäœããåºãå Žåã®ã¿ã¯ã©ã¹ãåé€
if (e.target === dragDropArea) {
dragDropArea.classList.remove('drag-over');
}
});
// ãããã
dragDropArea.addEventListener('drop', function(e) {
e.preventDefault();
e.stopPropagation();
dragDropArea.classList.remove('drag-over');
console.log('ãã¡ã€ã«ããããã€ãã³ãçºç');
console.log('e.dataTransfer:', e.dataTransfer);
const files = e.dataTransfer.files;
console.log('ããããããããã¡ã€ã«æ°:', files ? files.length : 'undefined');
console.log('files ãªããžã§ã¯ã:', files);
if (!files || files.length === 0) {
alert('â ïž ãã¡ã€ã«ãæ€åºã§ããŸããã§ãã\n\nããäžåºŠã詊ããã ããã\n\néåžžã®ãã¡ã€ã«éžæãã¿ã³ãã詊ããã ããã');
console.error('ããããããããã¡ã€ã«ãèŠã€ãããŸãã');
console.error('e.dataTransfer.files:', e.dataTransfer.files);
console.error('e.dataTransfer.items:', e.dataTransfer.items);
return;
}
if (files.length > 1) {
alert('â ïž è€æ°ã®ãã¡ã€ã«ãéžæãããŠããŸã\n\n1ã€ã®CSVãã¡ã€ã«ã®ã¿ãéžæããŠãã ããã');
console.warn('è€æ°ãã¡ã€ã«æ€åº:', files.length, 'å');
return;
}
const file = files[0];
console.log('ãã¡ã€ã«è©³çް:', {
name: file.name,
size: file.size + ' bytes',
type: file.type,
lastModified: new Date(file.lastModified).toLocaleString('ja-JP')
});
if (validateFile(file)) {
// DataTransferãªããžã§ã¯ãã䜿ã£ãŠãã¡ã€ã«å
¥åã«èšå®
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
displayFileInfo(file);
console.log('â ãã¡ã€ã«èšå®å®äº');
} else {
console.error('ãã¡ã€ã«æ€èšŒå€±æ:', file.name);
}
});
// ãã¡ã€ã«éžæïŒéåžžã®æ¹æ³ïŒ
fileInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
displayFileInfo(file);
}
});
// ãã¡ã€ã«æ
å ±ã衚瀺
function displayFileInfo(file) {
const fileSizeBytes = file.size;
let fileSizeText;
if (fileSizeBytes === 0) {
fileSizeText = '0 KB (ãã¡ã€ã«ã空ã§ã)';
alert('â ïž ãšã©ãŒ: ãã¡ã€ã«ãµã€ãºã0ãã€ãã§ã\n\nãã¡ã€ã«å: ' + file.name + '\n\nå¯èœãªåå :\n1. ãã¡ã€ã«ã空ã§ã\n2. ãã¡ã€ã«ãç ŽæããŠããŸã\n3. ãã¡ã€ã«ãžã®ã¢ã¯ã»ã¹æš©éããããŸãã\n\nå¥ã®ãã¡ã€ã«ãéžæãããããã¡ã€ã«ã確èªããŠãã ããã');
} else if (fileSizeBytes < 1024) {
fileSizeText = fileSizeBytes + ' B';
} else if (fileSizeBytes < 1024 * 1024) {
fileSizeText = (fileSizeBytes / 1024).toFixed(2) + ' KB';
} else {
fileSizeText = (fileSizeBytes / 1024 / 1024).toFixed(2) + ' MB';
}
fileName.textContent = file.name;
fileSize.textContent = fileSizeText;
selectedFileInfo.classList.add('show');
dragDropArea.classList.add('has-file');
// ãã©ãã°ãšãªã¢ã®ããã¹ããæŽæ°
const dragDropText = dragDropArea.querySelector('.drag-drop-text');
const dragDropSubtext = dragDropArea.querySelector('.drag-drop-subtext');
dragDropText.textContent = 'ãã¡ã€ã«ãéžæãããŸãã';
dragDropSubtext.innerHTML = '<strong>ã¯ãªãã¯</strong>ããŠå¥ã®ãã¡ã€ã«ãéžæ';
}
// ãã¡ã€ã«æ€èšŒ
function validateFile(file) {
console.log('ãã¡ã€ã«æ€èšŒéå§:', {
name: file.name,
size: file.size,
type: file.type,
lastModified: new Date(file.lastModified)
});
// ãã¡ã€ã«ãµã€ãºã0ãã€ããã§ãã¯
if (file.size === 0) {
alert('â ãšã©ãŒ: ãã¡ã€ã«ã空ã§ã\n\nãã¡ã€ã«å: ' + file.name + '\nãã¡ã€ã«ãµã€ãº: 0ãã€ã\n\nãã¡ã€ã«ã®å
容ã確èªããŠãã ããã');
console.error('ãã¡ã€ã«ãµã€ãºã0ãã€ã:', file.name);
return false;
}
// ãã¡ã€ã«ãµã€ãºãã§ãã¯
if (file.size > <?= UPLOAD_MAX_SIZE ?>) {
alert('â ãšã©ãŒ: ãã¡ã€ã«ãµã€ãºã倧ããããŸã\n\nãã¡ã€ã«å: ' + file.name + '\nãã¡ã€ã«ãµã€ãº: ' + (file.size / 1024 / 1024).toFixed(2) + ' MB\n\n10MB以äžã®ãã¡ã€ã«ãéžæããŠãã ããã');
console.error('ãã¡ã€ã«ãµã€ãºè¶
é:', file.size, 'bytes');
return false;
}
// ãã¡ã€ã«åœ¢åŒãã§ãã¯
const allowedTypes = ['text/csv', 'application/csv', 'text/plain'];
const fileName = file.name.toLowerCase();
const hasValidExtension = fileName.endsWith('.csv');
const hasValidMimeType = allowedTypes.includes(file.type);
if (!hasValidExtension && !hasValidMimeType) {
alert('â ãšã©ãŒ: CSVãã¡ã€ã«ã®ã¿ã¢ããããŒãå¯èœã§ã\n\nãã¡ã€ã«å: ' + file.name + '\nãã¡ã€ã«åœ¢åŒ: ' + (file.type || 'äžæ') + '\n\n.csv圢åŒã®ãã¡ã€ã«ãéžæããŠãã ããã');
console.error('ç¡å¹ãªãã¡ã€ã«åœ¢åŒ:', { type: file.type, name: file.name });
return false;
}
console.log('â ãã¡ã€ã«æ€èšŒæå');
return true;
}
// ãã©ãŒã éä¿¡
document.getElementById('uploadForm').addEventListener('submit', function(e) {
const uploadBtn = document.getElementById('uploadBtn');
const uploadProgress = document.getElementById('uploadProgress');
console.log('ãã©ãŒã éä¿¡éå§');
if (!fileInput.files.length) {
alert('â CSVãã¡ã€ã«ãéžæããŠãã ããã');
console.error('ãã¡ã€ã«ãéžæãããŠããŸãã');
e.preventDefault();
return;
}
const file = fileInput.files[0];
console.log('éä¿¡ãããã¡ã€ã«:', {
name: file.name,
size: file.size + ' bytes',
type: file.type
});
if (file.size === 0) {
alert('â ãšã©ãŒ: ãã¡ã€ã«ã空ã§ã\n\nãã¡ã€ã«å: ' + file.name + '\n\n空ã®ãã¡ã€ã«ã¯ã¢ããããŒãã§ããŸããã');
console.error('空ã®ãã¡ã€ã«ãéžæãããŠããŸã');
e.preventDefault();
return;
}
if (!validateFile(file)) {
console.error('ãã¡ã€ã«æ€èšŒå€±æ');
e.preventDefault();
return;
}
// ã¢ããããŒãéå§
console.log('â ã¢ããããŒãéå§');
uploadBtn.disabled = true;
uploadBtn.textContent = 'ã¢ããããŒãäž...';
uploadProgress.style.display = 'block';
// æ¬äŒŒçãªããã°ã¬ã¹ããŒ
let progress = 0;
const progressInterval = setInterval(function() {
progress += 5;
document.getElementById('progressBar').style.width = progress + '%';
if (progress >= 90) {
clearInterval(progressInterval);
}
}, 100);
});
</script>
</div> <!-- page-container -->
</div> <!-- main-content -->
<?php
require_once 'includes/footer.php';
exit;
}
// POSTãªã¯ãšã¹ãã®åŠç
// CSRFããŒã¯ã³ã®æ€èšŒ
if (!isset($_POST['csrf_token']) || !validateCsrfToken($_POST['csrf_token'])) {
setErrorMessage('CSRFããŒã¯ã³ãç¡å¹ã§ã');
header('Location: upload.php');
exit;
}
try {
// ãã¡ã€ã«ã¢ããããŒãã®æ€èšŒ
if (!isset($_FILES['csv_file']) || $_FILES['csv_file']['error'] !== UPLOAD_ERR_OK) {
$error_messages = [
UPLOAD_ERR_INI_SIZE => 'ãã¡ã€ã«ãµã€ãºã倧ããããŸã',
UPLOAD_ERR_FORM_SIZE => 'ãã¡ã€ã«ãµã€ãºã倧ããããŸã',
UPLOAD_ERR_PARTIAL => 'ãã¡ã€ã«ã®ã¢ããããŒããå®äºããŠããŸãã',
UPLOAD_ERR_NO_FILE => 'ãã¡ã€ã«ãéžæãããŠããŸãã',
UPLOAD_ERR_NO_TMP_DIR => 'ãã³ãã©ãªãã£ã¬ã¯ããªãèŠã€ãããŸãã',
UPLOAD_ERR_CANT_WRITE => 'ãã¡ã€ã«ã®æžã蟌ã¿ã«å€±æããŸãã',
UPLOAD_ERR_EXTENSION => 'ãã¡ã€ã«ã®ã¢ããããŒãã忢ãããŸãã'
];
$error_code = $_FILES['csv_file']['error'];
$error_message = isset($error_messages[$error_code]) ?
$error_messages[$error_code] :
'äžæãªã¢ããããŒããšã©ãŒãçºçããŸãã';
throw new Exception($error_message);
}
$uploaded_file = $_FILES['csv_file'];
// ãã¡ã€ã«ãµã€ãºã®æ€èšŒ
if ($uploaded_file['size'] > UPLOAD_MAX_SIZE) {
throw new Exception('ãã¡ã€ã«ãµã€ãºãå¶éãè¶
ããŠããŸãïŒæå€§: ' . (UPLOAD_MAX_SIZE / 1024 / 1024) . 'MBïŒ');
}
// ãã¡ã€ã«åœ¢åŒã®æ€èšŒ
$file_info = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($file_info, $uploaded_file['tmp_name']);
finfo_close($file_info);
$file_extension = strtolower(pathinfo($uploaded_file['name'], PATHINFO_EXTENSION));
if (!in_array($mime_type, UPLOAD_ALLOWED_TYPES) && $file_extension !== 'csv') {
throw new Exception('CSVãã¡ã€ã«ä»¥å€ã¯ã¢ããããŒãã§ããŸãã');
}
// ã¢ããããŒããã£ã¬ã¯ããªã®äœæ
if (!is_dir(UPLOAD_DIR)) {
if (!mkdir(UPLOAD_DIR, 0755, true)) {
throw new Exception('ã¢ããããŒããã£ã¬ã¯ããªã®äœæã«å€±æããŸãã');
}
}
// ãã¡ã€ã«åã®ãµãã¿ã€ãº
$original_filename = $uploaded_file['name'];
$sanitized_filename = date('Y-m-d_H-i-s') . '_' . sanitizeFilename($original_filename);
$upload_path = UPLOAD_DIR . $sanitized_filename;
// ãã¡ã€ã«ãç§»å
if (!move_uploaded_file($uploaded_file['tmp_name'], $upload_path)) {
throw new Exception('ãã¡ã€ã«ã®ä¿åã«å€±æããŸãã');
}
// UTF-8 BOM ãå
é ã«ããå Žåã¯é€å»ããïŒCSVåŠçåïŒ
$file_contents = @file_get_contents($upload_path);
if ($file_contents !== false && substr($file_contents, 0, 3) === "\xEF\xBB\xBF") {
if (file_put_contents($upload_path, substr($file_contents, 3)) === false) {
throw new Exception('ãã¡ã€ã«ã®ä¿ååŸã®BOMé€å»ã«å€±æããŸãã');
}
error_log("Removed UTF-8 BOM from uploaded file: " . $upload_path);
}
// CSVãã¡ã€ã«ã®åŠç
$csv_processor = new CsvProcessor();
if (!$csv_processor->loadFile($upload_path)) {
$errors = $csv_processor->getErrors();
throw new Exception('CSVãã¡ã€ã«ã®èªã¿èŸŒã¿ã«å€±æããŸãã: ' . implode(', ', $errors));
}
// CSVããŒã¿ã®æ€èšŒ
if (!$csv_processor->validate()) {
$errors = $csv_processor->getErrors();
throw new Exception('CSVããŒã¿ã®æ€èšŒã«å€±æããŸãã: ' . implode(', ', $errors));
}
// ããŒã¿ããŒã¹æ¥ç¶
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
// ããŒã¿ããŒã¹åæåïŒå¿
é ããŒãã«ã®ååšç¢ºèªãšäœæïŒ
$db_initializer = new DatabaseInitializer($database);
error_log("Initializing required database tables");
$init_results = $db_initializer->initializeAllTables();
if (!empty($init_results['tables_created'])) {
error_log("Created tables: " . implode(', ', $init_results['tables_created']));
}
if (!empty($init_results['errors'])) {
throw new Exception('ããŒã¿ããŒã¹åæåãšã©ãŒ: ' . implode(', ', $init_results['errors']));
}
$device_manager = new DeviceManager($database);
$activity_logger = new ActivityLogger($database); // ãã°èšé²çš
// CSVããŒã¿ãããŒã¿ããŒã¹ã«ç»é²
$results = $device_manager->processCsvData($csv_processor);
// åŠççµæã®ç¢ºèª
if (!$results['success']) {
throw new Exception('ããŒã¿ããŒã¹ãžã®ç»é²ã«å€±æããŸãã: ' . implode(', ', $results['errors']));
}
// CSVããŒã¿ãããµãŒãã¹-è£
眮皮å¥ã®ãªã¬ãŒã·ã§ã³ãèªåç»é²ïŒå¥ãã©ã³ã¶ã¯ã·ã§ã³ïŒ
$relationCount = 0;
try {
$csvData = $csv_processor->getData();
$processedRelations = [];
error_log("Starting relation registration for " . count($csvData) . " records");
foreach ($csvData as $row) {
$serviceName = $row['ãµãŒãã¹å'];
$deviceType = $row['è£
眮皮å¥'];
// éè€ãã§ãã¯ïŒåäžåŠçå
ã§ã®éè€ãé¿ããïŒ
$relationKey = $serviceName . '|' . $deviceType;
if (!in_array($relationKey, $processedRelations)) {
try {
$description = "CSVèªåç»é²: " . date('Y-m-d H:i:s') . " - " . basename($original_filename);
error_log("Registering relation: {$serviceName} -> {$deviceType}");
$device_manager->registerServiceDeviceTypeRelation($serviceName, $deviceType, $description);
$relationCount++;
$processedRelations[] = $relationKey;
error_log("Successfully registered relation: {$relationKey}");
} catch (Exception $e) {
// ãªã¬ãŒã·ã§ã³ç»é²ã®ãšã©ãŒã¯èŠåãšããŠæ±ãïŒåŠçã¯ç¶ç¶ïŒ
error_log("Relation registration warning for {$relationKey}: " . $e->getMessage());
}
} else {
error_log("Skipping duplicate relation: {$relationKey}");
}
}
error_log("Completed relation registration. Total: {$relationCount} relations");
} catch (Exception $e) {
// ãªã¬ãŒã·ã§ã³ç»é²ãšã©ãŒã¯èŠåãšããŠæ±ã
error_log("Relation registration process error: " . $e->getMessage());
$relationCount = 0;
}
// çµ±èšæ
å ±ã®ååŸ
$statistics = $csv_processor->getStatistics();
// æåã¡ãã»ãŒãžã®èšå®
$success_message = "CSVãã¡ã€ã«ã®åŠçãå®äºããŸããã\n";
$success_message .= "- åŠçã¬ã³ãŒãæ°: " . $results['device_info_count'] . "ä»¶\n";
$success_message .= "- äœæãããåçããŒãã«: " . count($results['dynamic_tables_created']) . "å\n";
if (!empty($results['dynamic_tables_created'])) {
$success_message .= "- ããŒãã«å: " . implode(', ', $results['dynamic_tables_created']) . "\n";
}
$success_message .= "- ãµãŒãã¹æ°: " . count($statistics['services']) . "çš®é¡\n";
$success_message .= "- è£
çœ®çš®å¥æ°: " . count($statistics['device_types']) . "çš®é¡\n";
$success_message .= "- èªåç»é²ããããªã¬ãŒã·ã§ã³: " . $relationCount . "ä»¶";
setSuccessMessage($success_message);
// æäœãã°ãèšé²
$logDetail = sprintf(
'ãã¡ã€ã«: %s, ã¬ã³ãŒãæ°: %d, ãµãŒãã¹æ°: %d, è£
çœ®çš®å¥æ°: %d',
$original_filename,
$results['device_info_count'],
count($statistics['services']),
count($statistics['device_types'])
);
$activity_logger->log(
getLoggedInUsername() ?? 'unknown',
ActivityLogger::ACTION_UPLOAD,
$logDetail
);
// ã¢ããããŒãããããã¡ã€ã«ãåé€ïŒãªãã·ã§ã³ïŒ
// unlink($upload_path);
} catch (Exception $e) {
// 詳现ãªãšã©ãŒæ
å ±ããã°ã«èšé²
$errorDetails = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
];
error_log("Upload error details: " . json_encode($errorDetails, JSON_UNESCAPED_UNICODE));
// ãããã°çšïŒãã¡ã€ã«ã«ãæžãåºã
@file_put_contents('/tmp/fusion_upload_debug.log',
date('Y-m-d H:i:s') . "\n" .
json_encode($errorDetails, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n\n",
FILE_APPEND
);
setErrorMessage("ã¢ããããŒãåŠçäžã«ãšã©ãŒãçºçããŸãã: " . $e->getMessage());
// ããŒã¿ããŒã¹ã®ãã©ã³ã¶ã¯ã·ã§ã³ç¶æ
ã確èªããŠããŒã«ããã¯
if (isset($database)) {
try {
if ($database->inTransaction()) {
$database->rollBack();
error_log("Transaction rolled back due to error");
}
} catch (Exception $rollbackError) {
error_log("Rollback error: " . $rollbackError->getMessage());
}
}
// ãšã©ãŒæã¯ã¢ããããŒãããããã¡ã€ã«ãåé€
if (isset($upload_path) && file_exists($upload_path)) {
unlink($upload_path);
}
} finally {
// ããŒã¿ããŒã¹æ¥ç¶ãéãã
if (isset($database)) {
try {
$database->close();
} catch (Exception $closeError) {
error_log("Database close error: " . $closeError->getMessage());
}
}
}
// çµæããŒãžã«ãªãã€ã¬ã¯ã
header('Location: upload.php');
exit;
?>
<?php
/**
* Ajax API ãšã³ããã€ã³ã
* ããã³ããšã³ãããã®éåæãªã¯ãšã¹ããåŠç
*/
require_once 'config.php';
require_once __DIR__ . '/includes/auth_helper.php';
// ãã°ã€ã³å¿
é
if (!isLoggedIn()) {
header('Content-Type: application/json; charset=UTF-8');
echo json_encode(['success' => false, 'error' => 'ãã°ã€ã³ãå¿
èŠã§ã']);
exit;
}
// JSON圢åŒã§åºå
header('Content-Type: application/json; charset=UTF-8');
// CORS察å¿ïŒå¿
èŠã«å¿ããŠïŒ
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: Content-Type');
try {
// ãªã¯ãšã¹ãã¡ãœããã®ç¢ºèª
$method = $_SERVER['REQUEST_METHOD'];
if ($method !== 'GET' && $method !== 'POST') {
throw new Exception('èš±å¯ãããŠããªããªã¯ãšã¹ãã¡ãœããã§ã');
}
// ã¢ã¯ã·ã§ã³ãã©ã¡ãŒã¿ã®ååŸ
$action = $_GET['action'] ?? $_POST['action'] ?? '';
if (empty($action)) {
throw new Exception('ã¢ã¯ã·ã§ã³ãæå®ãããŠããŸãã');
}
// ããŒã¿ããŒã¹æ¥ç¶
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
$deviceManager = new DeviceManager($database);
$response = ['success' => false, 'data' => null, 'message' => ''];
switch ($action) {
case 'get_services':
// ãµãŒãã¹åäžèЧããªã¬ãŒã·ã§ã³ããŒãã«ããååŸ
$services = $deviceManager->getServiceNamesFromRelation();
$response = [
'success' => true,
'data' => $services,
'message' => 'ãµãŒãã¹åäžèЧãååŸããŸãã'
];
break;
case 'get_device_types':
// è£
眮皮å¥äžèЧããªã¬ãŒã·ã§ã³ããŒãã«ããååŸïŒãµãŒãã¹åã§ãã£ã«ã¿ïŒ
$serviceName = $_GET['service_name'] ?? $_POST['service_name'] ?? null;
$deviceTypes = $deviceManager->getDeviceTypesFromRelation($serviceName);
$response = [
'success' => true,
'data' => $deviceTypes,
'message' => 'è£
眮皮å¥äžèЧãååŸããŸãã'
];
break;
case 'get_all_relations':
// å
šãªã¬ãŒã·ã§ã³ååŸ
$relations = $deviceManager->getAllRelations();
$response = [
'success' => true,
'data' => $relations,
'message' => 'ãªã¬ãŒã·ã§ã³äžèЧãååŸããŸãã'
];
break;
case 'build_relations':
// æ¢åããŒã¿ãããªã¬ãŒã·ã§ã³æ§ç¯
$result = $deviceManager->buildRelationsFromExistingData();
$response = [
'success' => $result['success'],
'data' => null,
'message' => $result['message']
];
break;
case 'search_devices':
// è£
眮æ
å ±æ€çŽ¢
$serviceName = $_GET['service_name'] ?? $_POST['service_name'] ?? null;
$deviceType = $_GET['device_type'] ?? $_POST['device_type'] ?? null;
$deviceName = $_GET['device_name'] ?? $_POST['device_name'] ?? null;
$page = (int)($_GET['page'] ?? $_POST['page'] ?? 1);
$limit = 20; // 1ããŒãžãããã®ä»¶æ°
$offset = ($page - 1) * $limit;
// 空æååãnullã«å€æ
$serviceName = $serviceName === '' ? null : $serviceName;
$deviceType = $deviceType === '' ? null : $deviceType;
$deviceName = $deviceName === '' ? null : $deviceName;
$devices = $deviceManager->searchDevicesAdvanced(
$serviceName,
$deviceType,
$deviceName,
$limit,
$offset
);
$total = $deviceManager->countDevicesAdvanced(
$serviceName,
$deviceType,
$deviceName
);
$totalPages = ceil($total / $limit);
$response = [
'success' => true,
'data' => [
'devices' => $devices,
'pagination' => [
'current_page' => $page,
'total_pages' => $totalPages,
'total_count' => $total,
'per_page' => $limit
]
],
'message' => "æ€çŽ¢çµæ: {$total}ä»¶èŠã€ãããŸãã"
];
break;
case 'get_statistics':
// è£
çœ®çµ±èšæ
å ±ãååŸ
$stats = $deviceManager->getDeviceStatistics();
$response = [
'success' => true,
'data' => $stats,
'message' => 'çµ±èšæ
å ±ãååŸããŸãã'
];
break;
case 'build_relations':
// æ¢åããŒã¿ãããªã¬ãŒã·ã§ã³ãèªåæ§ç¯
$buildResult = $deviceManager->buildRelationsFromExistingData();
$response = [
'success' => true,
'data' => $buildResult,
'message' => "ãªã¬ãŒã·ã§ã³æ§ç¯å®äº: {$buildResult['registered']}ä»¶ç»é²"
];
break;
case 'get_all_relations':
// å
šãªã¬ãŒã·ã§ã³äžèЧãååŸïŒç®¡ççšïŒ
$relations = $deviceManager->getAllRelations();
$response = [
'success' => true,
'data' => $relations,
'message' => 'ãªã¬ãŒã·ã§ã³äžèЧãååŸããŸãã'
];
break;
case 'generate_teraterm_macro':
// Teratermãã¯ãçæ
$deviceIp = $_GET['device_ip'] ?? '';
$username = $_GET['username'] ?? '';
$password = $_GET['password'] ?? '';
$deviceName = $_GET['device_name'] ?? 'device';
if (empty($deviceIp) || empty($username) || empty($password)) {
throw new Exception('IPã¢ãã¬ã¹ããŠãŒã¶ãŒåããã¹ã¯ãŒããå¿
èŠã§ã');
}
require_once 'classes/TeratermMacroGenerator.php';
$generator = new TeratermMacroGenerator($deviceIp, $username, $password);
// ãã¡ã€ã«åãçæïŒå®å
šãªæåã«å€æïŒ
$safeName = preg_replace('/[^a-zA-Z0-9._-]/', '_', $deviceName);
$filename = "{$safeName}_{$deviceIp}.ttl";
// JSONã¬ã¹ãã³ã¹ã§ã¯ãªãããã¡ã€ã«ãšããŠããŠã³ããŒã
header('Content-Type: text/plain; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
$macroContent = $generator->generate();
// æäœãã°
try {
$actLogger = new ActivityLogger($database);
$actLogger->log(
getLoggedInUsername() ?? 'unknown',
ActivityLogger::ACTION_TERATERM,
'device: ' . $deviceName . ' (' . $deviceIp . ')'
);
} catch (Exception $logEx) {
error_log('Teraterm log error: ' . $logEx->getMessage());
}
echo $macroContent;
exit; // JSONåºåãã¹ããã
case 'get_device':
// è£
眮æ
å ±ãååŸ
$primaryKey = $_GET['primary_key'] ?? $_POST['primary_key'] ?? '';
if (empty($primaryKey)) {
throw new Exception('Primary keyãæå®ãããŠããŸãã');
}
$device = $deviceManager->getDeviceByPrimaryKey($primaryKey);
if (!$device) {
throw new Exception('æå®ãããè£
眮æ
å ±ãèŠã€ãããŸãã');
}
// åçããŒãã«ã®ããŒã¿ãååŸ
$tableName = sanitizeTableName($device['service_name'] . '_' . $device['device_type']);
$extendedData = [];
$extendedColumns = [];
if ($deviceManager->dynamicTableExists($tableName)) {
$dynamicData = $deviceManager->getDynamicTableData($tableName, $primaryKey);
if ($dynamicData) {
$extendedColumns = $deviceManager->getDynamicTableExtendedColumns($tableName);
// æ¡åŒµåã®ããŒã¿ã®ã¿ãæœåº
foreach ($extendedColumns as $col) {
$extendedData[$col] = $dynamicData[$col] ?? null;
}
}
}
$response = [
'success' => true,
'data' => [
'device' => $device,
'extended_data' => $extendedData,
'extended_columns' => $extendedColumns
],
'message' => 'è£
眮æ
å ±ãååŸããŸãã'
];
break;
case 'update_device':
// è£
眮æ
å ±ãæŽæ°
$primaryKey = $_POST['primary_key'] ?? '';
$oldServiceName = $_POST['old_service_name'] ?? '';
$oldDeviceType = $_POST['old_device_type'] ?? '';
if (empty($primaryKey)) {
throw new Exception('Primary keyãæå®ãããŠããŸãã');
}
// æŽæ°ããŒã¿ãååŸ
$updateData = [
'service_name' => $_POST['service_name'] ?? '',
'device_type' => $_POST['device_type'] ?? '',
'device_name' => $_POST['device_name'] ?? '',
'login_ip' => $_POST['login_ip'] ?? null,
'username1' => $_POST['username1'] ?? ''
];
// ãã¹ã¯ãŒã1-10ããŠãŒã¶ãŒå2-10ã远å
$updateData['password1'] = $_POST['password1'] ?? null;
for ($i = 2; $i <= 10; $i++) {
$updateData["username{$i}"] = $_POST["username{$i}"] ?? null;
$updateData["password{$i}"] = $_POST["password{$i}"] ?? null;
}
// å¿
é é
ç®ãã§ãã¯
if (empty($updateData['service_name']) || empty($updateData['device_type']) ||
empty($updateData['device_name']) || empty($updateData['username1'])) {
throw new Exception('ãµãŒãã¹åãè£
眮皮å¥ãè£
眮åç§°ããŠãŒã¶ãŒå1ã¯å¿
é ã§ã');
}
// device_infoããŒãã«ãæŽæ°
$deviceManager->updateDeviceInfo($primaryKey, $updateData);
// åçããŒãã«ãæŽæ°
$oldTableName = sanitizeTableName($oldServiceName . '_' . $oldDeviceType);
$newTableName = sanitizeTableName($updateData['service_name'] . '_' . $updateData['device_type']);
// ãµãŒãã¹åãŸãã¯è£
眮皮å¥ã倿Žãããå Žåãå€ãããŒãã«ããåé€
if ($oldTableName !== $newTableName && !empty($oldServiceName) && !empty($oldDeviceType)) {
if ($deviceManager->dynamicTableExists($oldTableName)) {
$deviceManager->deleteFromDynamicTable($oldTableName, $primaryKey);
}
}
// æ°ããåçããŒãã«ã«æ¿å
¥ãŸãã¯æŽæ°
if ($deviceManager->dynamicTableExists($newTableName)) {
// åçããŒãã«çšã®ããŒã¿ãæºåïŒprimary_keyãšæ¡åŒµã«ã©ã ã®ã¿ïŒ
$dynamicData = [
'primary_key' => $primaryKey
];
// æ¡åŒµåã®ããŒã¿ã远å ïŒextended_ã§å§ãŸãPOSTãã©ã¡ãŒã¿ïŒ
$extendedColumns = $deviceManager->getDynamicTableExtendedColumns($newTableName);
foreach ($extendedColumns as $col) {
$postKey = "extended_{$col}";
if (isset($_POST[$postKey])) {
$dynamicData[$col] = $_POST[$postKey];
}
}
// æ¡åŒµã«ã©ã ãããå Žåã®ã¿åçããŒãã«ãæŽæ°
if (count($dynamicData) > 1) {
$deviceManager->insertOrUpdateDynamicData($newTableName, $dynamicData);
}
}
// æäœãã°
$actLogger = new ActivityLogger($database);
$actLogger->log(
getLoggedInUsername() ?? 'unknown',
ActivityLogger::ACTION_UPDATE_DEVICE,
'primary_key: ' . $primaryKey
);
$response = [
'success' => true,
'data' => null,
'message' => 'è£
眮æ
å ±ãæŽæ°ããŸãã'
];
break;
case 'delete_device':
// è£
眮æ
å ±ãåé€
$primaryKey = $_POST['primary_key'] ?? '';
$serviceName = $_POST['service_name'] ?? '';
$deviceType = $_POST['device_type'] ?? '';
if (empty($primaryKey)) {
throw new Exception('Primary keyãæå®ãããŠããŸãã');
}
// device_infoããåé€
$deviceManager->deleteDeviceInfo($primaryKey);
// åçããŒãã«ãããåé€
if (!empty($serviceName) && !empty($deviceType)) {
$tableName = sanitizeTableName($serviceName . '_' . $deviceType);
if ($deviceManager->dynamicTableExists($tableName)) {
$deviceManager->deleteFromDynamicTable($tableName, $primaryKey);
}
}
// æäœãã°
$actLogger = new ActivityLogger($database);
$actLogger->log(
getLoggedInUsername() ?? 'unknown',
ActivityLogger::ACTION_DELETE_DEVICE,
'primary_key: ' . $primaryKey . ', service: ' . $serviceName . ', type: ' . $deviceType
);
$response = [
'success' => true,
'data' => null,
'message' => 'è£
眮æ
å ±ãåé€ããŸãã'
];
break;
default:
throw new Exception('äžæ£ãªã¢ã¯ã·ã§ã³ã§ã: ' . $action);
}
} catch (Exception $e) {
$response = [
'success' => false,
'data' => null,
'message' => $e->getMessage()
];
// ãšã©ãŒãã°ã«èšé²ïŒæ¬çªç°å¢ã§ã¯éèŠïŒ
error_log("Ajax API Error: " . $e->getMessage());
// HTTPã¹ããŒã¿ã¹ã³ãŒããèšå®
http_response_code(400);
} finally {
// ããŒã¿ããŒã¹æ¥ç¶ãéãã
if (isset($database)) {
$database->close();
}
}
// JSON圢åŒã§ã¬ã¹ãã³ã¹ãåºå
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
?>
<?php
require_once 'config.php';
require_once __DIR__ . '/includes/auth_helper.php';
// ãã°ã€ã³å¿
é
requireLogin();
$pageTitle = 'CSVããŠã³ããŒã - è£
眮æ
å ±ç®¡çã·ã¹ãã ';
try {
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
$deviceManager = new DeviceManager($database);
// ãµãŒãã¹åãšããã€ã¹çš®å¥ãååŸ
$services = $deviceManager->getServiceNamesFromRelation();
$selectedService = $_GET['service_name'] ?? $_POST['service_name'] ?? '';
$selectedDeviceType = $_GET['device_type'] ?? $_POST['device_type'] ?? '';
// éžæããããµãŒãã¹ã«å¯Ÿå¿ããããã€ã¹çš®å¥ãååŸ
$deviceTypes = [];
if (!empty($selectedService)) {
$deviceTypes = $deviceManager->getDeviceTypesFromRelation($selectedService);
}
// ãã¬ãã¥ãŒããŒã¿
$previewData = [];
$tableName = '';
$totalCount = 0;
$previewColumns = [];
if (!empty($selectedService) && !empty($selectedDeviceType)) {
// åçããŒãã«åãçæ
$tableName = sanitizeTableName($selectedService . '_' . $selectedDeviceType);
if ($deviceManager->dynamicTableExists($tableName)) {
// åçããŒãã«ã®ã«ã©ã ãååŸïŒprimary_key, created_at, updated_at ãé€ãïŒ
$dynamicColumnsResult = $database->getTableColumns($tableName);
$dynamicColumns = array_column($dynamicColumnsResult, 'COLUMN_NAME');
$excludeColumns = ['primary_key', 'created_at', 'updated_at'];
$extendedColumns = array_diff($dynamicColumns, $excludeColumns);
// ãã¬ãã¥ãŒçšã®ã«ã©ã ããããŒãäœæ
$previewColumns = [
'ãµãŒãã¹å', 'è£
眮皮å¥', 'è£
眮åç§°', 'ãã°ã€ã³IP',
'ãŠãŒã¶å1', 'ãã¹ã¯ãŒã1',
'ãŠãŒã¶å2', 'ãã¹ã¯ãŒã2',
'ãŠãŒã¶å3', 'ãã¹ã¯ãŒã3',
'ãŠãŒã¶å4', 'ãã¹ã¯ãŒã4',
'ãŠãŒã¶å5', 'ãã¹ã¯ãŒã5',
'ãŠãŒã¶å6', 'ãã¹ã¯ãŒã6',
'ãŠãŒã¶å7', 'ãã¹ã¯ãŒã7',
'ãŠãŒã¶å8', 'ãã¹ã¯ãŒã8',
'ãŠãŒã¶å9', 'ãã¹ã¯ãŒã9',
'ãŠãŒã¶å10', 'ãã¹ã¯ãŒã10'
];
$previewColumns = array_merge($previewColumns, $extendedColumns);
// JOINã¯ãšãªã§ããŒã¿ãååŸïŒæåã®10ä»¶ïŒ
$dynamicColumnList = [];
foreach ($extendedColumns as $col) {
$dynamicColumnList[] = "dt.`{$col}`";
}
$dynamicColumnStr = !empty($dynamicColumnList) ? ', ' . implode(', ', $dynamicColumnList) : '';
$sql = "
SELECT
di.service_name as 'ãµãŒãã¹å',
di.device_type as 'è£
眮皮å¥',
di.device_name as 'è£
眮åç§°',
di.login_ip as 'ãã°ã€ã³IP',
di.username1 as 'ãŠãŒã¶å1',
di.password1 as 'ãã¹ã¯ãŒã1',
di.username2 as 'ãŠãŒã¶å2',
di.password2 as 'ãã¹ã¯ãŒã2',
di.username3 as 'ãŠãŒã¶å3',
di.password3 as 'ãã¹ã¯ãŒã3',
di.username4 as 'ãŠãŒã¶å4',
di.password4 as 'ãã¹ã¯ãŒã4',
di.username5 as 'ãŠãŒã¶å5',
di.password5 as 'ãã¹ã¯ãŒã5',
di.username6 as 'ãŠãŒã¶å6',
di.password6 as 'ãã¹ã¯ãŒã6',
di.username7 as 'ãŠãŒã¶å7',
di.password7 as 'ãã¹ã¯ãŒã7',
di.username8 as 'ãŠãŒã¶å8',
di.password8 as 'ãã¹ã¯ãŒã8',
di.username9 as 'ãŠãŒã¶å9',
di.password9 as 'ãã¹ã¯ãŒã9',
di.username10 as 'ãŠãŒã¶å10',
di.password10 as 'ãã¹ã¯ãŒã10'
{$dynamicColumnStr}
FROM device_info di
LEFT JOIN `{$tableName}` dt ON di.primary_key = dt.primary_key
WHERE di.service_name = ? AND di.device_type = ?
ORDER BY di.created_at DESC
LIMIT 10
";
$stmt = $database->execute($sql, [$selectedService, $selectedDeviceType]);
$previewData = $stmt->fetchAll();
// å
šè¡ã空æ¬ã®ã«ã©ã ãé€å€
if (!empty($previewData)) {
$columnsToKeep = [];
foreach ($previewColumns as $index => $columnName) {
$hasValue = false;
foreach ($previewData as $row) {
$values = array_values($row);
$value = $values[$index] ?? '';
// å€ãååšãã空ã§ãªãå Žå
if ($value !== null && trim((string)$value) !== '') {
$hasValue = true;
break;
}
}
if ($hasValue) {
$columnsToKeep[] = $index;
}
}
// ä¿æããã«ã©ã ã®ã¿ã«çµã蟌ã¿
$previewColumns = array_values(array_intersect_key($previewColumns, array_flip($columnsToKeep)));
// ãã¬ãã¥ãŒããŒã¿ãä¿æããã«ã©ã ã®ã¿ã«çµã蟌ã¿
$filteredPreviewData = [];
foreach ($previewData as $row) {
$values = array_values($row);
$filteredRow = [];
foreach ($columnsToKeep as $index) {
$filteredRow[] = $values[$index] ?? '';
}
$filteredPreviewData[] = $filteredRow;
}
$previewData = $filteredPreviewData;
}
// ç·ä»¶æ°ãååŸ
$countSql = "
SELECT COUNT(*) as total
FROM device_info di
LEFT JOIN `{$tableName}` dt ON di.primary_key = dt.primary_key
WHERE di.service_name = ? AND di.device_type = ?
";
$countStmt = $database->execute($countSql, [$selectedService, $selectedDeviceType]);
$totalCount = $countStmt->fetchColumn();
}
}
// CSVããŠã³ããŒãåŠç
if ($_POST['action'] ?? '' === 'download_csv') {
$service = $_POST['service_name'] ?? '';
$deviceType = $_POST['device_type'] ?? '';
if (!empty($service) && !empty($deviceType)) {
$tableName = sanitizeTableName($service . '_' . $deviceType);
if ($deviceManager->dynamicTableExists($tableName)) {
// åçããŒãã«ã®ã«ã©ã ãååŸïŒprimary_key, created_at, updated_at ãé€ãïŒ
$dynamicColumnsResult = $database->getTableColumns($tableName);
$dynamicColumns = array_column($dynamicColumnsResult, 'COLUMN_NAME');
$excludeColumns = ['primary_key', 'created_at', 'updated_at'];
$extendedColumns = array_diff($dynamicColumns, $excludeColumns);
// JOINã¯ãšãªã§ããŒã¿ãååŸ
$dynamicColumnList = [];
foreach ($extendedColumns as $col) {
$dynamicColumnList[] = "dt.`{$col}`";
}
$dynamicColumnStr = !empty($dynamicColumnList) ? ', ' . implode(', ', $dynamicColumnList) : '';
$sql = "
SELECT
di.service_name as 'ãµãŒãã¹å',
di.device_type as 'è£
眮皮å¥',
di.device_name as 'è£
眮åç§°',
di.login_ip as 'ãã°ã€ã³IP',
di.username1 as 'ãŠãŒã¶å1',
di.password1 as 'ãã¹ã¯ãŒã1',
di.username2 as 'ãŠãŒã¶å2',
di.password2 as 'ãã¹ã¯ãŒã2',
di.username3 as 'ãŠãŒã¶å3',
di.password3 as 'ãã¹ã¯ãŒã3',
di.username4 as 'ãŠãŒã¶å4',
di.password4 as 'ãã¹ã¯ãŒã4',
di.username5 as 'ãŠãŒã¶å5',
di.password5 as 'ãã¹ã¯ãŒã5',
di.username6 as 'ãŠãŒã¶å6',
di.password6 as 'ãã¹ã¯ãŒã6',
di.username7 as 'ãŠãŒã¶å7',
di.password7 as 'ãã¹ã¯ãŒã7',
di.username8 as 'ãŠãŒã¶å8',
di.password8 as 'ãã¹ã¯ãŒã8',
di.username9 as 'ãŠãŒã¶å9',
di.password9 as 'ãã¹ã¯ãŒã9',
di.username10 as 'ãŠãŒã¶å10',
di.password10 as 'ãã¹ã¯ãŒã10'
{$dynamicColumnStr}
FROM device_info di
LEFT JOIN `{$tableName}` dt ON di.primary_key = dt.primary_key
WHERE di.service_name = ? AND di.device_type = ?
ORDER BY di.created_at DESC
";
$stmt = $database->execute($sql, [$service, $deviceType]);
$data = $stmt->fetchAll();
if (!empty($data)) {
// å
šè¡ã空æ¬ã®ã«ã©ã ãé€å€
$allColumns = array_keys($data[0]);
$columnsToKeep = [];
foreach ($allColumns as $columnName) {
$hasValue = false;
foreach ($data as $row) {
$value = $row[$columnName] ?? '';
// å€ãååšãã空ã§ãªãå Žå
if ($value !== null && trim((string)$value) !== '') {
$hasValue = true;
break;
}
}
if ($hasValue) {
$columnsToKeep[] = $columnName;
}
}
// ããŒã¿ãä¿æããã«ã©ã ã®ã¿ã«çµã蟌ã¿
$filteredData = [];
foreach ($data as $row) {
$filteredRow = [];
foreach ($columnsToKeep as $columnName) {
$filteredRow[$columnName] = $row[$columnName] ?? '';
}
$filteredData[] = $filteredRow;
}
$data = $filteredData;
// éžæããããã£ãŒã«ããååŸïŒé€å€ãããã«ã©ã ã¯é€ãïŒ
$selectedFields = $_POST['selected_fields'] ?? [];
$selectedFields = array_intersect($selectedFields, $columnsToKeep);
// ãããã°: éžæããããã£ãŒã«ãããã°åºå
error_log("=== CSV Download Debug ===");
error_log("POST data: " . print_r($_POST, true));
error_log("Selected fields: " . print_r($selectedFields, true));
error_log("Available fields: " . print_r(array_keys($data[0] ?? []), true));
// CSVãã¡ã€ã«åãçæ
$filename = $service . '_' . $deviceType . '_' . date('Y-m-d_H-i-s') . '.csv';
// HTTPããããŒãèšå®
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: no-cache, must-revalidate');
// BOMãåºåïŒExcelã§ã®æååã察çïŒ
echo "\xEF\xBB\xBF";
// CSVããŒã¿ãåºå
$output = fopen('php://output', 'w');
// ããããŒè¡ãåºåïŒéžæããããã£ãŒã«ãã®ã¿ïŒ
if (!empty($selectedFields)) {
fputcsv($output, $selectedFields);
// ããŒã¿è¡ãåºåïŒéžæããããã£ãŒã«ãã®ã¿ïŒ
foreach ($data as $row) {
$filteredRow = [];
foreach ($selectedFields as $field) {
$filteredRow[] = $row[$field] ?? '';
}
fputcsv($output, $filteredRow);
}
} else {
// éžæãã£ãŒã«ãããªãå Žåã¯å
šãã£ãŒã«ãåºå
$headers = array_keys($data[0]);
fputcsv($output, $headers);
foreach ($data as $row) {
fputcsv($output, $row);
}
}
fclose($output);
// æäœãã°ãèšé²ïŒããããŒéä¿¡åãªã®ã§çŽæ¥DBã«æžãïŒ
try {
$logDb = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_CHARSET, DB_TYPE, DB_PORT);
$logger = new ActivityLogger($logDb);
$logger->log(
getLoggedInUsername() ?? 'unknown',
ActivityLogger::ACTION_DOWNLOAD,
'ãã¡ã€ã«: ' . $filename . ', ä»¶æ°: ' . count($data) . 'ä»¶'
);
$logDb->close();
} catch (Exception $logEx) {
error_log('Download log error: ' . $logEx->getMessage());
}
exit;
} else {
setErrorMessage('ããŠã³ããŒãããããŒã¿ããããŸããã');
}
} else {
setErrorMessage('æå®ãããããŒãã«ãååšããŸããã');
}
} else {
setErrorMessage('ãµãŒãã¹åãšè£
眮皮å¥ãéžæããŠãã ããã');
}
}
} catch (Exception $e) {
setErrorMessage("ãšã©ãŒãçºçããŸãã: " . $e->getMessage());
$services = [];
$deviceTypes = [];
}
$errorMessage = getErrorMessage();
$successMessage = getSuccessMessage();
// å
±éããããŒãèªã¿èŸŒã¿
require_once 'includes/header.php';
?>
<div class="main-content">
<div class="page-container">
<div class="page-header">
<h1 class="page-title">
<div class="page-title-icon black-svg">
<?php include 'svgs/download.svg'; ?>
</div>
CSVããŠã³ããŒã
</h1>
</div>
<?php if ($errorMessage): ?>
<div class="alert alert-error">
<svg class="alert-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"/>
</svg>
<div>
<strong>ãšã©ãŒ:</strong> <?= h($errorMessage) ?>
</div>
</div>
<?php endif; ?>
<?php if ($successMessage): ?>
<div class="alert alert-success">
<svg class="alert-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z"/>
</svg>
<div>
<strong>æå:</strong> <?= h($successMessage) ?>
</div>
</div>
<?php endif; ?>
<div class="alert alert-info">
<h4 style="display: flex; align-items: center; gap: 8px;">
<span style="width: 20px; height: 20px; display: inline-flex;"><?php include 'svgs/info.svg'; ?></span>
CSVããŠã³ããŒãæ©èœã«ã€ããŠ
</h4>
<p>
ãµãŒãã¹åãšè£
眮皮å¥ãéžæãããšã該åœããè£
眮ããŒã¿ãCSV圢åŒã§ããŠã³ããŒãã§ããŸãã
ããŠã³ããŒãåã«ããŒã¿ã®ãã¬ãã¥ãŒã確èªã§ããŸãã
</p>
</div>
<!-- æ€çŽ¢ã»ããŠã³ããŒããã©ãŒã -->
<div class="form-section">
<h3>
<div class="nav-icon">
<?php include 'svgs/search.svg'; ?>
</div>
ããŠã³ããŒã察象ã®éžæ
</h3>
<form method="post" id="searchForm">
<div class="form-group">
<label for="service_name">ãµãŒãã¹å:</label>
<select name="service_name" id="service_name" class="form-control">
<option value="">-- ãµãŒãã¹åãéžæ --</option>
<?php foreach ($services as $service): ?>
<option value="<?= h($service) ?>" <?= $service === $selectedService ? 'selected' : '' ?>>
<?= h($service) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="device_type">è£
眮皮å¥:</label>
<select name="device_type" id="device_type" class="form-control" onchange="updatePreview()">
<option value="">-- è£
眮皮å¥ãéžæ --</option>
<?php foreach ($deviceTypes as $deviceType): ?>
<option value="<?= h($deviceType) ?>" <?= $deviceType === $selectedDeviceType ? 'selected' : '' ?>>
<?= h($deviceType) ?>
</option>
<?php endforeach; ?>
</select>
</div>
</form>
</div>
<!-- ããŒã¿ãã¬ãã¥ãŒ -->
<?php if (!empty($selectedService) && !empty($selectedDeviceType)): ?>
<div class="preview-section">
<?php if ($deviceManager->dynamicTableExists($tableName)): ?>
<div class="table-info">
<h4>ð ããŒãã«æ
å ±</h4>
<p>
<strong>ããŒãã«å:</strong> <?= h($tableName) ?><br>
<strong>ç·ããŒã¿æ°:</strong> <?= number_format($totalCount) ?>ä»¶<br>
<strong>ãã¬ãã¥ãŒ:</strong> æåã®<?= count($previewData) ?>ä»¶ã衚瀺
</p>
</div>
<?php if (!empty($previewData)): ?>
<div style="margin-bottom: 15px;">
<p style="font-weight: bold; color: #333; margin-bottom: 5px;">
<span class="svg-wrapper" style="width: 20px; height: 20px; display: inline-flex;"><?php include 'svgs/info.svg'; ?></span>
äžèŠãªã«ã©ã ã¯ãã§ãã¯ãå€ããŠãã ãã
</p>
<p style="color: #666; font-size: 0.95em;">â»åºåãããCSVã¯è¡åéã«ãªããŸã</p>
</div>
<div style="overflow-x: auto;">
<table id="previewTable" class="preview-table transposed">
<?php
// ããŒã¿ãé転ãããŠè¡šç€º
if (!empty($previewData)) {
$transposedData = [];
// åã«ã©ã ã®ããŒã¿ãéãã
foreach ($previewColumns as $colIndex => $columnName) {
$transposedData[$columnName] = [];
foreach ($previewData as $row) {
$values = array_values($row);
$transposedData[$columnName][] = $values[$colIndex] ?? '';
}
}
// é転衚瀺
echo '<thead><tr>';
echo '<th class="checkbox-header"><input type="checkbox" id="select_all" checked onchange="toggleAllCheckboxes(this.checked)"></th>';
echo '<th class="row-header">é
ç®</th>';
for ($i = 0; $i < count($previewData); $i++) {
echo '<th>ããŒã¿' . ($i + 1) . '</th>';
}
echo '</tr></thead>';
echo '<tbody>';
foreach ($transposedData as $fieldName => $fieldValues) {
$fieldId = 'field_' . md5($fieldName); // ãŠããŒã¯ãªIDçæ
echo '<tr class="clickable-row" data-checkbox-id="' . $fieldId . '" style="cursor: pointer;">';
echo '<td class="checkbox-cell" onclick="event.stopPropagation();"><input type="checkbox" id="' . $fieldId . '" name="selected_fields[]" value="' . h($fieldName) . '" checked></td>';
echo '<th class="row-header">' . h($fieldName) . '</th>';
foreach ($fieldValues as $value) {
echo '<td title="' . h($value) . '">' . h($value) . '</td>';
}
echo '</tr>';
}
echo '</tbody>';
}
?>
</table>
</div>
<!-- ããŠã³ããŒããã¿ã³ -->
<div class="download-section">
<h4>
CSVããŠã³ããŒã
</h4>
<p id="download-info">å
š<?= number_format($totalCount) ?>ä»¶ã®ããŒã¿ãCSVãã¡ã€ã«ãšããŠããŠã³ããŒãããŸãã</p>
<form method="post" id="downloadForm">
<input type="hidden" name="action" value="download_csv">
<input type="hidden" name="service_name" value="<?= h($selectedService) ?>">
<input type="hidden" name="device_type" value="<?= h($selectedDeviceType) ?>">
<!-- éžæããããã£ãŒã«ããããã«åçã«è¿œå -->
<button type="submit" class="btn btn-success">
<span style="width: 16px; height: 16px; display: inline-flex; vertical-align: middle; margin-right: 4px;"><?php include 'svgs/download.svg'; ?></span>
CSV ããŠã³ããŒã
</button>
</form>
</div>
<?php else: ?>
<div class="empty-state">
<h3>ããŒã¿ããããŸãã</h3>
<p>éžæããããµãŒãã¹ã»è£
眮皮å¥ã«å¯Ÿå¿ããããŒã¿ãèŠã€ãããŸããã</p>
</div>
<?php endif; ?>
<?php else: ?>
<div class="empty-state">
<h3>ããŒãã«ãååšããŸãã</h3>
<p>éžæããããµãŒãã¹ã»è£
眮皮å¥ã«å¯Ÿå¿ããããŒãã«ããŸã äœæãããŠããŸããã<br>
å
ã«CSVãã¡ã€ã«ãã¢ããããŒãããŠããŒã¿ãç»é²ããŠãã ããã</p>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (empty($selectedService) || empty($selectedDeviceType)): ?>
<div class="empty-state">
<h3 style="display: flex; align-items: center; gap: 8px; justify-content: center;">
<span style="width: 24px; height: 24px; display: inline-flex;"><?php include 'svgs/info.svg'; ?></span>
äœ¿ãæ¹
</h3>
<ol style="text-align: left; display: inline-block;">
<li>ãµãŒãã¹åãéžæããŠãã ãã</li>
<li>è£
眮皮å¥ãéžæããŠãã ãã</li>
<li>ããã¬ãã¥ãŒè¡šç€ºããã¿ã³ã§ããŒã¿ã確èª</li>
<li>ãCSVããŠã³ããŒãããã¿ã³ã§ãã¡ã€ã«ãååŸ</li>
</ol>
</div>
<?php endif; ?>
<script>
// å
±éfetch颿°ïŒã»ãã·ã§ã³Cookieéä¿¡ã®ãã credentials: 'same-origin' ãä»äžïŒ
async function apiFetch(url, options) {
return fetch(url, Object.assign({ credentials: 'same-origin' }, options));
}
// è£
眮皮å¥ã®æŽæ°
var updateDeviceTypes = async function() {
const serviceName = document.getElementById('service_name').value;
const deviceTypeSelect = document.getElementById('device_type');
// çŸåšã®éžæå€ãä¿å
const currentValue = deviceTypeSelect.value;
// è£
眮皮å¥ããªã»ãã
deviceTypeSelect.innerHTML = '<option value="">-- è£
眮皮å¥ãéžæ --</option>';
if (serviceName) {
try {
const response = await apiFetch(`ajax_api.php?action=get_device_types&service_name=${encodeURIComponent(serviceName)}`);
const result = await response.json();
if (result.success) {
result.data.forEach(deviceType => {
const option = document.createElement('option');
option.value = deviceType;
option.textContent = deviceType;
// 以åã®éžæå€ãšäžèŽããå Žåã¯éžæç¶æ
ã«ãã
if (deviceType === currentValue) {
option.selected = true;
}
deviceTypeSelect.appendChild(option);
});
}
} catch (error) {
console.error('è£
眮皮å¥ååŸãšã©ãŒ:', error);
}
}
}
// ãã¬ãã¥ãŒã®æŽæ°
function updatePreview() {
const serviceName = document.getElementById('service_name').value;
const deviceType = document.getElementById('device_type').value;
if (serviceName && deviceType) {
const params = new URLSearchParams();
params.append('service_name', serviceName);
params.append('device_type', deviceType);
window.location.href = '?' + params.toString();
}
}
// ããŒãžèªã¿èŸŒã¿æã«è£
眮皮å¥ãæŽæ°ïŒéžæç¶æ
ãä¿æïŒ
document.addEventListener('DOMContentLoaded', async function() {
// inline onchange ã®ä»£ããã« addEventListener ã§ç»é²ïŒå
šãã©ãŠã¶å¯Ÿå¿ïŒ
document.getElementById('service_name').addEventListener('change', updateDeviceTypes);
const serviceName = document.getElementById('service_name').value;
if (serviceName) {
await updateDeviceTypes();
}
});
// ãã§ãã¯ããã¯ã¹é¢é£æ©èœ
function toggleAllCheckboxes(checked) {
const checkboxes = document.querySelectorAll('input[name="selected_fields[]"]');
checkboxes.forEach(cb => cb.checked = checked);
updateDownloadButton();
}
function updateDownloadButton() {
const selectedFields = getSelectedFields();
const downloadBtn = document.querySelector('#downloadForm button');
if (downloadBtn) {
const iconHtml = downloadBtn.querySelector('span')?.outerHTML || '';
const baseText = 'CSV ããŠã³ããŒã';
const countText = selectedFields.length > 0 ? ` (${selectedFields.length}é
ç®)` : ' (é
ç®æªéžæ)';
downloadBtn.innerHTML = iconHtml + baseText + countText;
}
}
function getSelectedFields() {
const checkboxes = document.querySelectorAll('input[name="selected_fields[]"]:checked');
return Array.from(checkboxes).map(cb => cb.value);
}
function updateSelectedFields() {
// ãã§ãã¯ããã¯ã¹å€æŽæã«ããŠã³ããŒããã¿ã³ãæŽæ°
updateDownloadButton();
}
// CSVããŠã³ããŒãæã®ãã£ãŒã«ãéžæãèæ
®
document.addEventListener('DOMContentLoaded', function() {
const downloadForm = document.getElementById('downloadForm');
console.log('downloadForm found:', downloadForm);
if (downloadForm) {
downloadForm.addEventListener('submit', function(e) {
const selectedFields = getSelectedFields();
console.log('Submit event - Selected fields:', selectedFields);
if (selectedFields.length === 0) {
alert('å°ãªããšã1ã€ã®é
ç®ãéžæããŠãã ããã');
e.preventDefault();
return;
}
// æ¢åã®hiddenãã£ãŒã«ããã¯ãªã¢
const existingHidden = downloadForm.querySelectorAll('input[type="hidden"][name="selected_fields[]"]');
console.log('Existing hidden fields to remove:', existingHidden.length);
existingHidden.forEach(input => input.remove());
// éžæããããã£ãŒã«ãããã©ãŒã ã«è¿œå
selectedFields.forEach(field => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'selected_fields[]';
input.value = field;
downloadForm.appendChild(input);
console.log('Added hidden field:', field);
});
// æçµçãªãã©ãŒã ããŒã¿ã確èª
const formData = new FormData(downloadForm);
console.log('Final form data:');
for (let pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
});
}
// ãã§ãã¯ããã¯ã¹ã«ã€ãã³ããªã¹ããŒã远å
const checkboxes = document.querySelectorAll('input[name="selected_fields[]"]');
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', updateSelectedFields);
});
// ããã¹ãŠéžæ/è§£é€ããã§ãã¯ããã¯ã¹ã«ãã€ãã³ããªã¹ããŒ
const selectAllCheckbox = document.getElementById('select_all');
if (selectAllCheckbox) {
selectAllCheckbox.addEventListener('change', function() {
toggleAllCheckboxes(this.checked);
updateDownloadButton(); // ãã¿ã³ãæŽæ°
});
}
// åæè¡šç€ºæã«ããŠã³ããŒããã¿ã³ãæŽæ°
updateDownloadButton();
// è¡ã¯ãªãã¯ã§ãã§ãã¯ããã¯ã¹ã®ç¶æ
ã倿Ž
const clickableRows = document.querySelectorAll('.clickable-row');
clickableRows.forEach(row => {
row.addEventListener('click', function(e) {
// ãã§ãã¯ããã¯ã¹ã»ã«ä»¥å€ãã¯ãªãã¯ããå Žå
if (!e.target.closest('.checkbox-cell')) {
const checkboxId = this.getAttribute('data-checkbox-id');
const checkbox = document.getElementById(checkboxId);
if (checkbox) {
checkbox.checked = !checkbox.checked;
updateDownloadButton();
}
}
});
});
});
</script>
</div> <!-- page-container -->
</div> <!-- main-content -->
<?php require_once 'includes/footer.php'; ?>
<?php
/**
* audit_logs ããŒãã«äœæãã€ã°ã¬ãŒã·ã§ã³
* å®è¡: docker compose exec web php admin/create_audit_logs.php
*/
require_once __DIR__ . '/../config.php';
echo "=== audit_logs ããŒãã« ãã€ã°ã¬ãŒã·ã§ã³ ===\n\n";
try {
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_CHARSET, DB_TYPE, DB_PORT);
$conn = $database->connect();
$sql = "
CREATE TABLE IF NOT EXISTS audit_logs (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT 'ID',
username VARCHAR(100) NOT NULL COMMENT 'æäœãŠãŒã¶ãŒå',
action VARCHAR(50) NOT NULL COMMENT 'ã¢ã¯ã·ã§ã³çš®å¥',
detail TEXT COMMENT 'è£è¶³æ
å ±',
ip_address VARCHAR(45) COMMENT 'ã¯ã©ã€ã¢ã³ãIP',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'æäœæ¥æ',
INDEX idx_audit_username (username),
INDEX idx_audit_action (action),
INDEX idx_audit_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='ãŠãŒã¶ãŒæäœãã°'
";
$conn->exec($sql);
echo "â audit_logs ããŒãã«ãäœæããŸããïŒæ¢åã®å Žåã¯ã¹ãããïŒ\n";
// 確èª
$check = $conn->query("SELECT COUNT(*) FROM audit_logs")->fetchColumn();
echo "â çŸåšã®ãã°ä»¶æ°: {$check} ä»¶\n";
echo "\n=== ãã€ã°ã¬ãŒã·ã§ã³å®äº ===\n";
} catch (Exception $e) {
echo "â ãšã©ãŒ: " . $e->getMessage() . "\n";
exit(1);
}
<?php
/**
* ãŠãŒã¶ãŒæäœãã°èšé²ã¯ã©ã¹
* upload / download / manage (update_device, delete_device, teraterm) ã®æäœã
* audit_logs ããŒãã«ã«èšé²ããã
*/
class ActivityLogger {
private $db;
/** ãã°ã®ã¢ã¯ã·ã§ã³å®æ° */
const ACTION_UPLOAD = 'upload';
const ACTION_DOWNLOAD = 'download';
const ACTION_UPDATE_DEVICE = 'update_device';
const ACTION_DELETE_DEVICE = 'delete_device';
const ACTION_TERATERM = 'teraterm_download';
public function __construct(Database $database) {
$this->db = $database;
}
/**
* æäœãã°ãèšé²ãã
*
* @param string $username æäœãŠãŒã¶ãŒå
* @param string $action ã¢ã¯ã·ã§ã³çš®å¥ïŒACTION_* 宿°ãæšå¥šïŒ
* @param string|null $detail è£è¶³æ
å ±ïŒãã¡ã€ã«åã»å¯Ÿè±¡è£
眮 primary_key ãªã©ïŒ
* @param string|null $ip ã¯ã©ã€ã¢ã³ãIPã¢ãã¬ã¹ïŒçç¥æã¯èªåååŸïŒ
* @return void
*/
public function log(string $username, string $action, ?string $detail = null, ?string $ip = null): void {
try {
$resolvedIp = $ip ?? $this->getClientIp();
$this->db->execute(
"INSERT INTO audit_logs (username, action, detail, ip_address, created_at)
VALUES (:username, :action, :detail, :ip, NOW())",
[
':username' => $username,
':action' => $action,
':detail' => $detail,
':ip' => $resolvedIp,
]
);
} catch (Exception $e) {
// ãã°å€±æã¯ã¢ããªã®åŠçãæ¢ããªãïŒãšã©ãŒãã°ã®ã¿ïŒ
error_log("ActivityLogger::log error: " . $e->getMessage());
}
}
/**
* ã¯ã©ã€ã¢ã³ãIPãååŸïŒãªããŒã¹ãããã·ãèæ
®ïŒ
*/
private function getClientIp(): string {
$headers = [
'HTTP_X_FORWARDED_FOR',
'HTTP_X_REAL_IP',
'HTTP_CLIENT_IP',
'REMOTE_ADDR',
];
foreach ($headers as $h) {
if (!empty($_SERVER[$h])) {
$ip = trim(explode(',', $_SERVER[$h])[0]);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
}
return 'unknown';
}
/**
* ãã°äžèЧãååŸïŒç®¡çç»é¢çšïŒ
*
* @param int $limit
* @param int $offset
* @return array
*/
public function getLogs(int $limit = 100, int $offset = 0): array {
return $this->db->query(
"SELECT * FROM audit_logs ORDER BY created_at DESC LIMIT :limit OFFSET :offset",
[':limit' => $limit, ':offset' => $offset]
);
}
}
<?php
/**
* ã¢ããªã±ãŒã·ã§ã³èšå®ãã¡ã€ã«
* Dockerç°å¢çšèšå®ïŒdocker-compose.yml ã®ç°å¢å€æ°ã«å¯Ÿå¿ïŒ
*/
// ããŒã¿ããŒã¹èšå®ïŒç°å¢å€æ°å¯Ÿå¿ïŒ
define('DB_HOST', $_ENV['DB_HOST'] ?? 'mysql');
define('DB_NAME', $_ENV['DB_NAME'] ?? 'device_management');
define('DB_USER', $_ENV['DB_USER'] ?? 'root');
define('DB_PASS', $_ENV['DB_PASS'] ?? 'rootpassword');
define('DB_CHARSET', 'utf8mb4');
define('DB_TYPE', $_ENV['DB_TYPE'] ?? 'mysql');
define('DB_PORT', $_ENV['DB_PORT'] ?? null);
// ã¢ããããŒãèšå®
define('UPLOAD_MAX_SIZE', 10 * 1024 * 1024); // 10MB
define('UPLOAD_ALLOWED_TYPES', ['text/csv', 'application/csv', 'text/plain']);
define('UPLOAD_DIR', __DIR__ . '/uploads/');
// ãšã©ãŒã¬ããŒãèšå®ïŒæ¬çªç°å¢ã§ã¯ 0 ã«å€æŽïŒ
ini_set('display_errors', 1);
error_reporting(E_ALL);
// ãšã©ãŒãã°èšå®
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/logs/php_error.log');
// ã¿ã€ã ãŸãŒã³èšå®
date_default_timezone_set('Asia/Tokyo');
// ã»ãã·ã§ã³èšå®
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// ã¯ã©ã¹ãã¡ã€ã«ã®èªåèªã¿èŸŒã¿
spl_autoload_register(function ($class_name) {
$file = __DIR__ . '/classes/' . $class_name . '.php';
if (file_exists($file)) {
require_once $file;
}
});
// -------------------------------------------------------
// ãŠãŒãã£ãªãã£é¢æ°
// -------------------------------------------------------
/** ããŒãã«åã®ãµãã¿ã€ãº */
function sanitizeTableName($name) {
return preg_replace('/[^a-zA-Z0-9_]/', '_', $name);
}
/** HTMLåºåçšãšã¹ã±ãŒã */
function h($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
/** ãšã©ãŒã¡ãã»ãŒãžãã»ãã·ã§ã³ã«ä¿å */
function setErrorMessage($message) {
$_SESSION['error_message'] = $message;
}
/** ãšã©ãŒã¡ãã»ãŒãžãååŸããŠåé€ */
function getErrorMessage() {
if (isset($_SESSION['error_message'])) {
$message = $_SESSION['error_message'];
unset($_SESSION['error_message']);
return $message;
}
return null;
}
/** æåã¡ãã»ãŒãžãã»ãã·ã§ã³ã«ä¿å */
function setSuccessMessage($message) {
$_SESSION['success_message'] = $message;
}
/** æåã¡ãã»ãŒãžãååŸããŠåé€ */
function getSuccessMessage() {
if (isset($_SESSION['success_message'])) {
$message = $_SESSION['success_message'];
unset($_SESSION['success_message']);
return $message;
}
return null;
}
/** ãã¡ã€ã«åã®ãµãã¿ã€ãº */
function sanitizeFilename($filename) {
$filename = basename($filename);
$filename = preg_replace('/[^a-zA-Z0-9._-]/', '_', $filename);
return $filename;
}
/** CSVããããŒã®ãµãã¿ã€ãºïŒBOMã»ç¹æ®æåé€å»ïŒ */
function sanitizeColumnName($name) {
$name = str_replace("\xEF\xBB\xBF", '', $name); // BOMåé€
$name = trim($name);
$name = preg_replace('/[^\x20-\x7E]/', '', $name); // å¶åŸ¡æåãåé€
$name = preg_replace('/[^a-zA-Z0-9_\x80-\xFF]/', '_', $name);
return $name;
}
/** æ¡åŒµã«ã©ã ã®ããªããŒã·ã§ã³ */
function validateExtendedColumn($columnName) {
$reservedWords = [
'SELECT','INSERT','UPDATE','DELETE','DROP','CREATE','ALTER',
'TABLE','DATABASE','INDEX','PRIMARY','KEY','FOREIGN','REFERENCES',
'WHERE','FROM','JOIN','UNION','GROUP','ORDER','HAVING','LIMIT'
];
if (in_array(strtoupper($columnName), $reservedWords)) return false;
$fixedColumns = [
'primary_key','service_name','device_type','device_name',
'device_ip','username','password','created_at','updated_at'
];
if (in_array($columnName, $fixedColumns)) return false;
if (strlen($columnName) > 64) return false;
if (preg_match('/^\d/', $columnName)) return false;
return true;
}
/** CSRFããŒã¯ã³çæ */
function generateCsrfToken() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
/** CSRFããŒã¯ã³æ€èšŒ */
function validateCsrfToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
<?php
/**
* Databaseæ¥ç¶ç®¡çã¯ã©ã¹
*/
class Database {
private $connection;
private $host;
private $dbname;
private $username;
private $password;
private $charset;
private $dbType;
private $port;
private $inTransaction = false;
public function __construct($host, $dbname, $username, $password, $charset = 'utf8mb4', $dbType = 'mysql', $port = null) {
$this->host = $host;
$this->dbname = $dbname;
$this->username = $username;
$this->password = $password;
$this->charset = $charset;
$this->dbType = $dbType;
$this->port = $port ?: ($dbType === 'pgsql' ? 5432 : 3306);
}
/**
* ããŒã¿ããŒã¹ã¿ã€ããååŸ
* @return string
*/
public function getDbType() {
return $this->dbType;
}
/**
* ããŒã¿ããŒã¹ã«æ¥ç¶
* @return PDO
* @throws Exception
*/
public function connect() {
if ($this->connection === null) {
try {
// DSNçæïŒPostgreSQLãšMySQL察å¿ïŒ
if ($this->dbType === 'pgsql') {
$dsn = "pgsql:host={$this->host};port={$this->port};dbname={$this->dbname}";
} else {
$dsn = "mysql:host={$this->host};port={$this->port};dbname={$this->dbname};charset={$this->charset}";
}
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$this->connection = new PDO($dsn, $this->username, $this->password, $options);
// PostgreSQLã®å Žåã¯UTF-8ãšã³ã³ãŒãã£ã³ã°ãèšå®
if ($this->dbType === 'pgsql') {
$this->connection->exec("SET NAMES 'UTF8'");
}
} catch (PDOException $e) {
throw new Exception("ããŒã¿ããŒã¹æ¥ç¶ãšã©ãŒ: " . $e->getMessage());
}
}
return $this->connection;
}
/**
* æ¥ç¶ãéãã
*/
public function close() {
if ($this->connection !== null) {
// ã¢ã¯ãã£ããªãã©ã³ã¶ã¯ã·ã§ã³ãããã°ããŒã«ããã¯
try {
if ($this->connection->inTransaction()) {
error_log("Warning: Active transaction found during connection close, rolling back");
$this->connection->rollBack();
$this->inTransaction = false;
}
} catch (Exception $e) {
error_log("Error during transaction cleanup: " . $e->getMessage());
}
$this->connection = null;
}
}
/**
* ãã©ã³ã¶ã¯ã·ã§ã³éå§
*/
public function beginTransaction() {
$connection = $this->connect();
if (!$connection->inTransaction()) {
$result = $connection->beginTransaction();
if ($result) {
$this->inTransaction = true;
}
return $result;
}
return true; // æ¢ã«ãã©ã³ã¶ã¯ã·ã§ã³äž
}
/**
* ã³ããã
*/
public function commit() {
$connection = $this->connect();
if ($connection->inTransaction()) {
$result = $connection->commit();
$this->inTransaction = false;
return $result;
}
return true; // ã¢ã¯ãã£ããªãã©ã³ã¶ã¯ã·ã§ã³ããªã
}
/**
* ããŒã«ããã¯
*/
public function rollBack() {
$connection = $this->connect();
if ($connection->inTransaction()) {
$result = $connection->rollBack();
$this->inTransaction = false;
return $result;
}
return true; // ã¢ã¯ãã£ããªãã©ã³ã¶ã¯ã·ã§ã³ããªã
}
/**
* ãã©ã³ã¶ã¯ã·ã§ã³ç¶æ
ã確èª
*/
public function inTransaction() {
return $this->connect()->inTransaction();
}
/**
* ããªãã¢ãã¹ããŒãã¡ã³ãã®å®è¡
* @param string $query
* @param array $params
* @return PDOStatement
*/
public function execute($query, $params = []) {
try {
$stmt = $this->connect()->prepare($query);
$stmt->execute($params);
return $stmt;
} catch (PDOException $e) {
throw new Exception("ã¯ãšãªå®è¡ãšã©ãŒ: " . $e->getMessage() . " SQL: " . $query);
}
}
/**
* SQLã¯ãšãªãå®è¡ããŠçµæãé
åã§è¿ã
* @param string $query
* @param array $params
* @return array
*/
public function query($query, $params = []) {
try {
$stmt = $this->connect()->prepare($query);
$stmt->execute($params);
return $stmt->fetchAll();
} catch (PDOException $e) {
throw new Exception("ã¯ãšãªå®è¡ãšã©ãŒ: " . $e->getMessage() . " SQL: " . $query);
}
}
/**
* ããŒãã«ã®ååšç¢ºèª
* @param string $tableName
* @return bool
*/
public function tableExists($tableName) {
try {
$stmt = $this->execute(
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = ? AND table_name = ?",
[$this->dbname, $tableName]
);
return $stmt->fetchColumn() > 0;
} catch (Exception $e) {
throw new Exception("ããŒãã«ååšç¢ºèªãšã©ãŒ: " . $e->getMessage());
}
}
/**
* ããŒãã«ã®ã«ã©ã æ
å ±ãååŸ
* @param string $tableName
* @return array
*/
public function getTableColumns($tableName) {
try {
$stmt = $this->execute(
"SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT
FROM information_schema.columns
WHERE table_schema = ? AND table_name = ?
ORDER BY ORDINAL_POSITION",
[$this->dbname, $tableName]
);
return $stmt->fetchAll();
} catch (Exception $e) {
throw new Exception("ã«ã©ã æ
å ±ååŸãšã©ãŒ: " . $e->getMessage());
}
}
}
?>
<?php
/**
* Ajax API çŽæ¥ãã¹ã - ãã©ãŠã¶ã§éããŠåäœç¢ºèª
*/
require_once 'config.php';
require_once __DIR__ . '/includes/auth_helper.php';
// ãã°ã€ã³å¿
é
requireLogin();
header('Content-Type: text/html; charset=UTF-8');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ajax API ãã¹ã</title>
<style>
body { font-family: sans-serif; margin: 20px; background: #f5f5f5; }
.test-section { background: white; padding: 20px; margin: 20px 0; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
h2 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
.btn { padding: 10px 20px; margin: 5px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; }
.btn:hover { background: #2980b9; }
.result { background: #f9f9f9; padding: 15px; margin: 10px 0; border-left: 4px solid #3498db; }
.error { border-left-color: #e74c3c; background: #fadbd8; }
.success { border-left-color: #27ae60; background: #d5f4e6; }
pre { white-space: pre-wrap; word-wrap: break-word; }
.loading { color: #3498db; font-style: italic; }
</style>
</head>
<body>
<h1>𧪠Ajax API çŽæ¥ãã¹ã</h1>
<div class="test-section">
<h2>1ïžâ£ ãµãŒãã¹åååŸãã¹ã</h2>
<button class="btn" onclick="testGetServices()">ãµãŒãã¹åååŸ</button>
<div id="result1" class="result" style="display:none;"></div>
</div>
<div class="test-section">
<h2>2ïžâ£ è£
眮皮å¥ååŸãã¹ã</h2>
<p>ãŸããµãŒãã¹åãååŸããŠããå®è¡ããŠãã ãã</p>
<input type="text" id="serviceNameInput" placeholder="ãµãŒãã¹åãå
¥å" style="padding: 8px; width: 300px;">
<button class="btn" onclick="testGetDeviceTypes()">è£
眮皮å¥ååŸ</button>
<div id="result2" class="result" style="display:none;"></div>
</div>
<div class="test-section">
<h2>3ïžâ£ æ€çŽ¢API ãã¹ãïŒå
šä»¶ïŒ</h2>
<button class="btn" onclick="testSearchAll()">å
šä»¶æ€çŽ¢</button>
<div id="result3" class="result" style="display:none;"></div>
</div>
<div class="test-section">
<h2>4ïžâ£ æ€çŽ¢API ãã¹ãïŒæ¡ä»¶æå®ïŒ</h2>
<p>ãµãŒãã¹å: <input type="text" id="searchServiceName" placeholder="ãµãŒãã¹å" style="padding: 8px; width: 200px;"></p>
<p>è£
眮皮å¥: <input type="text" id="searchDeviceType" placeholder="è£
眮皮å¥" style="padding: 8px; width: 200px;"></p>
<p>è£
眮åç§°: <input type="text" id="searchDeviceName" placeholder="è£
眮åç§°ïŒéšåäžèŽïŒ" style="padding: 8px; width: 200px;"></p>
<button class="btn" onclick="testSearchWithConditions()">æ¡ä»¶æ€çŽ¢</button>
<div id="result4" class="result" style="display:none;"></div>
</div>
<div class="test-section">
<h2>ð çµ±åãã¹ã</h2>
<button class="btn" onclick="runAllTests()">å
šãã¹ãå®è¡</button>
<div id="result5" class="result" style="display:none;"></div>
</div>
<div class="test-section">
<h2>ð é¢é£ãªã³ã¯</h2>
<ul>
<li><a href="search.php" target="_blank">æ€çŽ¢ããŒãžïŒæ¬çªïŒ</a></li>
<li><a href="manage.php" target="_blank">管çããŒãžïŒæ¬çªïŒ</a></li>
<li><a href="debug_search.php">ãããã°ããŒã«</a></li>
<li><a href="index.php">ãããããŒãž</a></li>
</ul>
</div>
<script>
// ãŠãŒãã£ãªãã£é¢æ°
function showResult(elementId, content, type = 'info') {
const element = document.getElementById(elementId);
element.style.display = 'block';
element.className = 'result ' + type;
element.innerHTML = content;
}
function showLoading(elementId) {
showResult(elementId, '<span class="loading">â³ åŠçäž...</span>', 'info');
}
// 1. ãµãŒãã¹åååŸãã¹ã
async function testGetServices() {
showLoading('result1');
try {
console.log('ð Testing: ajax_api.php?action=get_services');
const response = await fetch('ajax_api.php?action=get_services');
const text = await response.text();
console.log('Response status:', response.status);
console.log('Response text:', text);
let result;
try {
result = JSON.parse(text);
} catch (e) {
throw new Error('JSON parse error: ' + e.message + '\n\nResponse: ' + text);
}
let html = '<h3>ã¬ã¹ãã³ã¹:</h3>';
html += '<pre>' + JSON.stringify(result, null, 2) + '</pre>';
if (result.success) {
html += '<h3>ååŸããããµãŒãã¹å:</h3>';
html += '<ul>';
if (result.data && result.data.length > 0) {
result.data.forEach(service => {
html += '<li>' + escapeHtml(service) + '</li>';
});
// æåã®ãµãŒãã¹åãèªåå
¥å
document.getElementById('serviceNameInput').value = result.data[0];
document.getElementById('searchServiceName').value = result.data[0];
} else {
html += '<li style="color: orange;">ããŒã¿ããããŸãã</li>';
}
html += '</ul>';
showResult('result1', html, 'success');
} else {
html += '<p style="color: red;">ãšã©ãŒ: ' + escapeHtml(result.message) + '</p>';
showResult('result1', html, 'error');
}
} catch (error) {
console.error('Error:', error);
const html = '<h3>ãšã©ãŒçºç:</h3><pre>' + escapeHtml(error.message) + '</pre>';
showResult('result1', html, 'error');
}
}
// 2. è£
眮皮å¥ååŸãã¹ã
async function testGetDeviceTypes() {
const serviceName = document.getElementById('serviceNameInput').value;
if (!serviceName) {
alert('ãµãŒãã¹åãå
¥åããŠãã ãã');
return;
}
showLoading('result2');
try {
const url = 'ajax_api.php?action=get_device_types&service_name=' + encodeURIComponent(serviceName);
console.log('ð Testing:', url);
const response = await fetch(url);
const text = await response.text();
console.log('Response status:', response.status);
console.log('Response text:', text);
let result;
try {
result = JSON.parse(text);
} catch (e) {
throw new Error('JSON parse error: ' + e.message + '\n\nResponse: ' + text);
}
let html = '<h3>ã¬ã¹ãã³ã¹:</h3>';
html += '<pre>' + JSON.stringify(result, null, 2) + '</pre>';
if (result.success) {
html += '<h3>ååŸãããè£
眮皮å¥:</h3>';
html += '<ul>';
if (result.data && result.data.length > 0) {
result.data.forEach(type => {
html += '<li>' + escapeHtml(type) + '</li>';
});
// æåã®è£
眮皮å¥ãèªåå
¥å
document.getElementById('searchDeviceType').value = result.data[0];
} else {
html += '<li style="color: orange;">ããŒã¿ããããŸãã</li>';
}
html += '</ul>';
showResult('result2', html, 'success');
} else {
html += '<p style="color: red;">ãšã©ãŒ: ' + escapeHtml(result.message) + '</p>';
showResult('result2', html, 'error');
}
} catch (error) {
console.error('Error:', error);
const html = '<h3>ãšã©ãŒçºç:</h3><pre>' + escapeHtml(error.message) + '</pre>';
showResult('result2', html, 'error');
}
}
// 3. å
šä»¶æ€çŽ¢ãã¹ã
async function testSearchAll() {
showLoading('result3');
try {
const url = 'ajax_api.php?action=search_devices&page=1';
console.log('ð Testing:', url);
const response = await fetch(url);
const text = await response.text();
console.log('Response status:', response.status);
console.log('Response text:', text);
let result;
try {
result = JSON.parse(text);
} catch (e) {
throw new Error('JSON parse error: ' + e.message + '\n\nResponse: ' + text);
}
let html = '<h3>ã¬ã¹ãã³ã¹:</h3>';
html += '<pre>' + JSON.stringify(result, null, 2) + '</pre>';
if (result.success && result.data) {
const count = result.data.pagination.total_count;
const devices = result.data.devices;
html += '<h3>æ€çŽ¢çµæ: ' + count + ' ä»¶</h3>';
if (devices && devices.length > 0) {
html += '<table border="1" cellpadding="5" style="margin-top: 10px;">';
html += '<tr><th>ãµãŒãã¹å</th><th>è£
眮皮å¥</th><th>è£
眮åç§°</th><th>ãã°ã€ã³IP</th><th>ãŠãŒã¶å1</th></tr>';
devices.forEach(device => {
html += '<tr>';
html += '<td>' + escapeHtml(device.service_name) + '</td>';
html += '<td>' + escapeHtml(device.device_type) + '</td>';
html += '<td>' + escapeHtml(device.device_name) + '</td>';
html += '<td>' + escapeHtml(device.login_ip || '-') + '</td>';
html += '<td>' + escapeHtml(device.username1) + '</td>';
html += '</tr>';
});
html += '</table>';
}
showResult('result3', html, 'success');
} else {
html += '<p style="color: red;">ãšã©ãŒ: ' + escapeHtml(result.message || 'äžæãªãšã©ãŒ') + '</p>';
showResult('result3', html, 'error');
}
} catch (error) {
console.error('Error:', error);
const html = '<h3>ãšã©ãŒçºç:</h3><pre>' + escapeHtml(error.message) + '</pre>';
showResult('result3', html, 'error');
}
}
// 4. æ¡ä»¶æ€çŽ¢ãã¹ã
async function testSearchWithConditions() {
showLoading('result4');
try {
const serviceName = document.getElementById('searchServiceName').value;
const deviceType = document.getElementById('searchDeviceType').value;
const deviceName = document.getElementById('searchDeviceName').value;
const params = new URLSearchParams();
params.append('action', 'search_devices');
params.append('page', '1');
if (serviceName) params.append('service_name', serviceName);
if (deviceType) params.append('device_type', deviceType);
if (deviceName) params.append('device_name', deviceName);
const url = 'ajax_api.php?' + params.toString();
console.log('ð Testing:', url);
const response = await fetch(url);
const text = await response.text();
console.log('Response status:', response.status);
console.log('Response text:', text);
let result;
try {
result = JSON.parse(text);
} catch (e) {
throw new Error('JSON parse error: ' + e.message + '\n\nResponse: ' + text);
}
let html = '<h3>æ€çŽ¢æ¡ä»¶:</h3>';
html += '<ul>';
html += '<li>ãµãŒãã¹å: ' + (serviceName || 'ïŒæå®ãªãïŒ') + '</li>';
html += '<li>è£
眮皮å¥: ' + (deviceType || 'ïŒæå®ãªãïŒ') + '</li>';
html += '<li>è£
眮åç§°: ' + (deviceName || 'ïŒæå®ãªãïŒ') + '</li>';
html += '</ul>';
html += '<h3>ã¬ã¹ãã³ã¹:</h3>';
html += '<pre>' + JSON.stringify(result, null, 2) + '</pre>';
if (result.success && result.data) {
const count = result.data.pagination.total_count;
html += '<h3>æ€çŽ¢çµæ: ' + count + ' ä»¶</h3>';
showResult('result4', html, 'success');
} else {
html += '<p style="color: red;">ãšã©ãŒ: ' + escapeHtml(result.message || 'äžæãªãšã©ãŒ') + '</p>';
showResult('result4', html, 'error');
}
} catch (error) {
console.error('Error:', error);
const html = '<h3>ãšã©ãŒçºç:</h3><pre>' + escapeHtml(error.message) + '</pre>';
showResult('result4', html, 'error');
}
}
// å
šãã¹ãå®è¡
async function runAllTests() {
showResult('result5', '<h3>å
šãã¹ããé æ¬¡å®è¡äž...</h3>', 'info');
let allResults = '<h3>çµ±åãã¹ãçµæ</h3>';
let allSuccess = true;
// ãã¹ã1
try {
const response = await fetch('ajax_api.php?action=get_services');
const result = await response.json();
if (result.success && result.data && result.data.length > 0) {
allResults += '<p>â ãµãŒãã¹åååŸ: <span style="color: green;">æå</span> (' + result.data.length + 'ä»¶)</p>';
} else {
allResults += '<p>â ãµãŒãã¹åååŸ: <span style="color: red;">倱æ</span></p>';
allSuccess = false;
}
} catch (e) {
allResults += '<p>â ãµãŒãã¹åååŸ: <span style="color: red;">ãšã©ãŒ</span> - ' + e.message + '</p>';
allSuccess = false;
}
// ãã¹ã2
try {
const response = await fetch('ajax_api.php?action=search_devices&page=1');
const result = await response.json();
if (result.success && result.data) {
allResults += '<p>â å
šä»¶æ€çŽ¢: <span style="color: green;">æå</span> (' + result.data.pagination.total_count + 'ä»¶)</p>';
} else {
allResults += '<p>â å
šä»¶æ€çŽ¢: <span style="color: red;">倱æ</span></p>';
allSuccess = false;
}
} catch (e) {
allResults += '<p>â å
šä»¶æ€çŽ¢: <span style="color: red;">ãšã©ãŒ</span> - ' + e.message + '</p>';
allSuccess = false;
}
if (allSuccess) {
allResults += '<h2 style="color: green;">â ãã¹ãŠã®ãã¹ããæåããŸããïŒ</h2>';
allResults += '<p><strong>æ€çŽ¢æ©èœã¯æ£åžžã«åäœããŠããŸãã</strong></p>';
allResults += '<p>ããã§ãæ€çŽ¢ããŒãžã§åäœããªãå Žåã以äžã確èªããŠãã ããïŒ</p>';
allResults += '<ul>';
allResults += '<li>ãã©ãŠã¶ã®ãã£ãã·ã¥ãã¯ãªã¢ïŒCtrl+Shift+DeleteïŒ</li>';
allResults += '<li>ãã©ãŠã¶ã®éçºè
ããŒã«ïŒF12ïŒã§JavaScriptãšã©ãŒã確èª</li>';
allResults += '<li>æ€çŽ¢ããŒãžïŒsearch.phpïŒã®JavaScriptãæ£ããèªã¿èŸŒãŸããŠããã確èª</li>';
allResults += '</ul>';
showResult('result5', allResults, 'success');
} else {
allResults += '<h2 style="color: red;">â äžéšã®ãã¹ãã倱æããŸãã</h2>';
showResult('result5', allResults, 'error');
}
}
// HTMLãšã¹ã±ãŒã
function escapeHtml(text) {
if (text === null || text === undefined) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
</script>
</body>
</html>
<?php
/**
* æ€çŽ¢æ©èœãããã°ããŒã«
*/
require_once 'config.php';
require_once __DIR__ . '/includes/auth_helper.php';
// ãã°ã€ã³å¿
é
requireLogin();
header('Content-Type: text/html; charset=UTF-8');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>æ€çŽ¢ãããã°ããŒã«</title>
<style>
body { font-family: sans-serif; margin: 20px; background: #f5f5f5; }
.section { background: white; padding: 20px; margin: 20px 0; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
h2 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
.success { color: green; }
.error { color: red; }
.warning { color: orange; }
table { width: 100%; border-collapse: collapse; margin: 10px 0; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background: #3498db; color: white; }
tr:nth-child(even) { background: #f9f9f9; }
.code { background: #f4f4f4; padding: 10px; border-left: 3px solid #3498db; margin: 10px 0; overflow-x: auto; }
pre { margin: 0; }
.count { font-size: 24px; font-weight: bold; color: #3498db; }
</style>
</head>
<body>
<h1>ð æ€çŽ¢æ©èœãããã°ããŒã«</h1>
<?php
try {
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
$deviceManager = new DeviceManager($database);
// ========== 1. ããŒã¿ããŒã¹æ¥ç¶ç¢ºèª ==========
echo "<div class='section'>";
echo "<h2>1ïžâ£ ããŒã¿ããŒã¹æ¥ç¶</h2>";
echo "<p class='success'>â æ¥ç¶æå</p>";
echo "<p>ããŒã¿ããŒã¹ã¿ã€ã: <strong>{$dbType}</strong></p>";
echo "</div>";
// ========== 2. ããŒãã«ååšç¢ºèª ==========
echo "<div class='section'>";
echo "<h2>2ïžâ£ ããŒãã«ååšç¢ºèª</h2>";
$tables = ['device_info', 'service_device_type_relations'];
foreach ($tables as $table) {
$exists = $database->tableExists($table);
$status = $exists ? "<span class='success'>â ååš</span>" : "<span class='error'>â äžååš</span>";
echo "<p>{$table}: {$status}</p>";
}
echo "</div>";
// ========== 3. ããŒã¿ä»¶æ°ç¢ºèª ==========
echo "<div class='section'>";
echo "<h2>3ïžâ£ ããŒã¿ä»¶æ°ç¢ºèª</h2>";
if ($database->tableExists('device_info')) {
$stmt = $database->execute("SELECT COUNT(*) as count FROM device_info");
$result = $stmt->fetch();
$count = $result['count'];
echo "<p>device_info ããŒãã«ã®ã¬ã³ãŒãæ°: <span class='count'>{$count}</span> ä»¶</p>";
if ($count == 0) {
echo "<p class='warning'>â ããŒã¿ãç»é²ãããŠããŸãããCSVãã¢ããããŒãããŠãã ããã</p>";
}
} else {
echo "<p class='error'>â device_info ããŒãã«ãååšããŸãã</p>";
}
if ($database->tableExists('service_device_type_relations')) {
$stmt = $database->execute("SELECT COUNT(*) as count FROM service_device_type_relations WHERE is_active = 1");
$result = $stmt->fetch();
$relationCount = $result['count'];
echo "<p>service_device_type_relations ã®æå¹ãªã¬ã³ãŒãæ°: <span class='count'>{$relationCount}</span> ä»¶</p>";
if ($relationCount == 0) {
echo "<p class='warning'>â ãªã¬ãŒã·ã§ã³ããŒã¿ãç»é²ãããŠããŸããã</p>";
}
}
echo "</div>";
// ========== 4. ãµã³ãã«ããŒã¿è¡šç€º ==========
if (isset($count) && $count > 0) {
echo "<div class='section'>";
echo "<h2>4ïžâ£ ãµã³ãã«ããŒã¿ïŒææ°5ä»¶ïŒ</h2>";
$stmt = $database->execute("SELECT * FROM device_info ORDER BY created_at DESC LIMIT 5");
$samples = $stmt->fetchAll();
if (!empty($samples)) {
echo "<table>";
echo "<tr>";
echo "<th>ãµãŒãã¹å</th>";
echo "<th>è£
眮皮å¥</th>";
echo "<th>è£
眮åç§°</th>";
echo "<th>ãã°ã€ã³IP</th>";
echo "<th>ãŠãŒã¶å1</th>";
echo "<th>ç»é²æ¥æ</th>";
echo "</tr>";
foreach ($samples as $row) {
echo "<tr>";
echo "<td>" . htmlspecialchars($row['service_name']) . "</td>";
echo "<td>" . htmlspecialchars($row['device_type']) . "</td>";
echo "<td>" . htmlspecialchars($row['device_name']) . "</td>";
echo "<td>" . htmlspecialchars($row['login_ip'] ?? '-') . "</td>";
echo "<td>" . htmlspecialchars($row['username1']) . "</td>";
echo "<td>" . htmlspecialchars($row['created_at']) . "</td>";
echo "</tr>";
}
echo "</table>";
}
echo "</div>";
}
// ========== 5. ãµãŒãã¹åäžèЧ ==========
echo "<div class='section'>";
echo "<h2>5ïžâ£ ãµãŒãã¹åäžèЧïŒãªã¬ãŒã·ã§ã³ïŒ</h2>";
try {
$services = $deviceManager->getServiceNamesFromRelation();
echo "<p>ååŸããããµãŒãã¹åæ°: <span class='count'>" . count($services) . "</span> ä»¶</p>";
if (!empty($services)) {
echo "<ul>";
foreach ($services as $service) {
echo "<li>" . htmlspecialchars($service) . "</li>";
}
echo "</ul>";
} else {
echo "<p class='warning'>â ãµãŒãã¹åãååŸã§ããŸãã</p>";
// device_infoããçŽæ¥ååŸã詊ã
if ($database->tableExists('device_info')) {
$stmt = $database->execute("SELECT DISTINCT service_name FROM device_info ORDER BY service_name");
$directServices = $stmt->fetchAll(PDO::FETCH_COLUMN);
if (!empty($directServices)) {
echo "<p class='warning'>â device_infoã«ã¯ä»¥äžã®ãµãŒãã¹åããããŸãïŒ</p>";
echo "<ul>";
foreach ($directServices as $service) {
echo "<li>" . htmlspecialchars($service) . "</li>";
}
echo "</ul>";
echo "<p><strong>åå :</strong> service_device_type_relations ããŒãã«ã«ããŒã¿ãç»é²ãããŠããŸããã</p>";
echo "<p><a href='rebuild_relations.php' style='background:#3498db;color:white;padding:10px 20px;text-decoration:none;border-radius:4px;'>ãªã¬ãŒã·ã§ã³ãåæ§ç¯</a></p>";
}
}
}
} catch (Exception $e) {
echo "<p class='error'>â ãšã©ãŒ: " . htmlspecialchars($e->getMessage()) . "</p>";
}
echo "</div>";
// ========== 6. è£
眮皮å¥äžèЧ ==========
if (!empty($services)) {
echo "<div class='section'>";
echo "<h2>6ïžâ£ è£
眮皮å¥äžèŠ§ïŒæåã®ãµãŒãã¹ïŒ</h2>";
try {
$firstService = $services[0];
$deviceTypes = $deviceManager->getDeviceTypesFromRelation($firstService);
echo "<p>ãµãŒãã¹å: <strong>" . htmlspecialchars($firstService) . "</strong></p>";
echo "<p>è£
çœ®çš®å¥æ°: <span class='count'>" . count($deviceTypes) . "</span> ä»¶</p>";
if (!empty($deviceTypes)) {
echo "<ul>";
foreach ($deviceTypes as $type) {
echo "<li>" . htmlspecialchars($type) . "</li>";
}
echo "</ul>";
} else {
echo "<p class='warning'>â è£
眮皮å¥ãååŸã§ããŸãã</p>";
}
} catch (Exception $e) {
echo "<p class='error'>â ãšã©ãŒ: " . htmlspecialchars($e->getMessage()) . "</p>";
}
echo "</div>";
}
// ========== 7. æ€çŽ¢APIãã¹ã ==========
echo "<div class='section'>";
echo "<h2>7ïžâ£ æ€çŽ¢APIãã¹ã</h2>";
// ãã¹ã1: å
šä»¶æ€çŽ¢
try {
echo "<h3>ãã¹ã1: å
šä»¶æ€çŽ¢ïŒæ¡ä»¶ãªãïŒ</h3>";
$devices = $deviceManager->searchDevicesAdvanced(null, null, null, 10, 0);
$total = $deviceManager->countDevicesAdvanced(null, null, null);
echo "<p>æ€çŽ¢çµæ: <span class='count'>{$total}</span> ä»¶ïŒè¡šç€º: " . count($devices) . " ä»¶ïŒ</p>";
if ($total > 0 && count($devices) > 0) {
echo "<p class='success'>â å
šä»¶æ€çŽ¢ã¯æ£åžžã«åäœããŠããŸã</p>";
} elseif ($total > 0 && count($devices) == 0) {
echo "<p class='error'>â ããŒã¿ã¯ååšãããååŸã§ããŸããïŒã¯ãšãªã«åé¡ãããå¯èœæ§ïŒ</p>";
} else {
echo "<p class='warning'>â ããŒã¿ãç»é²ãããŠããŸãã</p>";
}
} catch (Exception $e) {
echo "<p class='error'>â ãšã©ãŒ: " . htmlspecialchars($e->getMessage()) . "</p>";
echo "<div class='code'><pre>" . htmlspecialchars($e->getTraceAsString()) . "</pre></div>";
}
// ãã¹ã2: ãµãŒãã¹åã§çµã蟌ã¿
if (!empty($services)) {
try {
echo "<h3>ãã¹ã2: ãµãŒãã¹åã§çµã蟌ã¿ïŒ" . htmlspecialchars($services[0]) . "ïŒ</h3>";
$devices = $deviceManager->searchDevicesAdvanced($services[0], null, null, 10, 0);
$total = $deviceManager->countDevicesAdvanced($services[0], null, null);
echo "<p>æ€çŽ¢çµæ: <span class='count'>{$total}</span> ä»¶</p>";
if ($total > 0) {
echo "<p class='success'>â ãµãŒãã¹åã§ã®çµã蟌ã¿ã¯æ£åžžã«åäœããŠããŸã</p>";
} else {
echo "<p class='warning'>â 該åœããŒã¿ãªã</p>";
}
} catch (Exception $e) {
echo "<p class='error'>â ãšã©ãŒ: " . htmlspecialchars($e->getMessage()) . "</p>";
}
}
echo "</div>";
// ========== 8. Ajax APIãã¹ã ==========
echo "<div class='section'>";
echo "<h2>8ïžâ£ Ajax APIãã¹ã</h2>";
echo "<p>以äžã®ãªã³ã¯ã§çŽæ¥APIããã¹ãã§ããŸãïŒ</p>";
echo "<ul>";
echo "<li><a href='ajax_api.php?action=get_services' target='_blank'>ãµãŒãã¹åååŸAPI</a></li>";
if (!empty($services)) {
echo "<li><a href='ajax_api.php?action=get_device_types&service_name=" . urlencode($services[0]) . "' target='_blank'>è£
眮皮å¥ååŸAPIïŒ" . htmlspecialchars($services[0]) . "ïŒ</a></li>";
echo "<li><a href='ajax_api.php?action=search_devices&service_name=" . urlencode($services[0]) . "&page=1' target='_blank'>æ€çŽ¢APIïŒ" . htmlspecialchars($services[0]) . "ïŒ</a></li>";
}
echo "<li><a href='ajax_api.php?action=search_devices&page=1' target='_blank'>æ€çŽ¢APIïŒå
šä»¶ïŒ</a></li>";
echo "</ul>";
echo "</div>";
// ========== 蚺æçµæ ==========
echo "<div class='section' style='background: #e8f4f8;'>";
echo "<h2>ð¯ 蚺æçµæ</h2>";
$issues = [];
if (!isset($count) || $count == 0) {
$issues[] = "device_infoããŒãã«ã«ããŒã¿ãç»é²ãããŠããŸãã â CSVãã¢ããããŒãããŠãã ãã";
}
if (!isset($relationCount) || $relationCount == 0) {
$issues[] = "ãªã¬ãŒã·ã§ã³ããŒã¿ãç»é²ãããŠããŸãã â <a href='rebuild_relations.php'>ãªã¬ãŒã·ã§ã³åæ§ç¯</a>ãå®è¡ããŠãã ãã";
}
if (empty($services) && isset($count) && $count > 0) {
$issues[] = "ããŒã¿ã¯ååšããããªã¬ãŒã·ã§ã³ãæªç»é²ã§ã â <a href='rebuild_relations.php'>ãªã¬ãŒã·ã§ã³åæ§ç¯</a>ãå®è¡ããŠãã ãã";
}
if (empty($issues)) {
echo "<p class='success' style='font-size: 18px;'>â åé¡ã¯æ€åºãããŸããã§ããã</p>";
echo "<p>ããã§ãæ€çŽ¢ã§ããªãå Žåã¯ããã©ãŠã¶ã®éçºè
ããŒã«ïŒF12ïŒã§ã³ã³ãœãŒã«ãšã©ãŒã確èªããŠãã ããã</p>";
} else {
echo "<p style='font-size: 18px; color: #e74c3c;'>â 以äžã®åé¡ãèŠã€ãããŸããïŒ</p>";
echo "<ol>";
foreach ($issues as $issue) {
echo "<li>{$issue}</li>";
}
echo "</ol>";
}
echo "</div>";
$database->close();
} catch (Exception $e) {
echo "<div class='section'>";
echo "<h2 class='error'>â ãšã©ãŒ</h2>";
echo "<p class='error'>" . htmlspecialchars($e->getMessage()) . "</p>";
echo "<div class='code'><pre>" . htmlspecialchars($e->getTraceAsString()) . "</pre></div>";
echo "</div>";
}
?>
<div class="section">
<h2>ð é¢é£ãªã³ã¯</h2>
<ul>
<li><a href="index.php">ãããããŒãž</a></li>
<li><a href="search.php">æ€çŽ¢ããŒãž</a></li>
<li><a href="manage.php">管çããŒãž</a></li>
<li><a href="check_db_schema.php">ã¹ããŒã確èª</a></li>
<li><a href="rebuild_relations.php">ãªã¬ãŒã·ã§ã³åæ§ç¯</a></li>
</ul>
</div>
</body>
</html>
<?php
/**
* CSVãã¡ã€ã«åŠçã¯ã©ã¹
*/
class CsvProcessor {
private $filePath;
private $headers;
private $data;
private $errors;
private $delimiter;
public function __construct($filePath = null) {
$this->filePath = $filePath;
$this->headers = [];
$this->data = [];
$this->errors = [];
$this->delimiter = ',';
}
/**
* CSVã®åºåãæåãèªåæ€åºããïŒã«ã³ã or ã¿ãïŒ
* @param string $content ãã¡ã€ã«å
容
* @return string æ€åºãããåºåãæå
*/
private function detectDelimiter($content) {
$firstLine = strtok($content, "\n");
$tabCount = substr_count($firstLine, "\t");
$commaCount = substr_count($firstLine, ',');
return ($tabCount > $commaCount) ? "\t" : ',';
}
/**
* 䜿çšäžã®åºåãæåãè¿ã
* @return string
*/
public function getDelimiter() {
return $this->delimiter;
}
/**
* CSVãã¡ã€ã«ãèªã¿èŸŒã
* @param string $filePath
* @return bool
*/
public function loadFile($filePath) {
$this->filePath = $filePath;
$this->headers = [];
$this->data = [];
$this->errors = [];
if (!file_exists($filePath)) {
$this->errors[] = "ãã¡ã€ã«ãååšããŸãã: " . $filePath;
return false;
}
if (!is_readable($filePath)) {
$this->errors[] = "ãã¡ã€ã«ãèªã¿èŸŒããŸãã: " . $filePath;
return false;
}
// ãã¡ã€ã«ã®ãšã³ã³ãŒãã£ã³ã°ãæ€åºã»å€æ
$content = file_get_contents($filePath);
$encoding = mb_detect_encoding($content, ['UTF-8', 'SJIS', 'EUC-JP', 'ASCII'], true);
if ($encoding !== 'UTF-8') {
$content = mb_convert_encoding($content, 'UTF-8', $encoding);
file_put_contents($filePath, $content);
}
// åºåãæåãèªåæ€åºïŒã«ã³ã or ã¿ãïŒ
$this->delimiter = $this->detectDelimiter($content);
// CSVãã¡ã€ã«ãéã
$handle = fopen($filePath, 'r');
if ($handle === false) {
$this->errors[] = "CSVãã¡ã€ã«ãéããŸãã";
return false;
}
$rowNumber = 0;
while (($row = fgetcsv($handle, 0, $this->delimiter)) !== false) {
$rowNumber++;
if ($rowNumber === 1) {
// ããããŒè¡
$this->headers = array_map('trim', $row);
// å¿
é ã«ã©ã ã®ååšç¢ºèªïŒ4ã€ã®ã¿ïŒ
$requiredColumns = ['ãµãŒãã¹å', 'è£
眮皮å¥', 'è£
眮åç§°', 'ãŠãŒã¶å1'];
foreach ($requiredColumns as $required) {
if (!in_array($required, $this->headers)) {
$this->errors[] = "å¿
é ã«ã©ã ãäžè¶³ããŠããŸã: " . $required;
}
}
if (!empty($this->errors)) {
fclose($handle);
return false;
}
} else {
// ããŒã¿è¡
if (count($row) !== count($this->headers)) {
$this->errors[] = "è¡{$rowNumber}: ã«ã©ã æ°ãäžèŽããŸããïŒæåŸ
: " . count($this->headers) . ", å®é: " . count($row) . "ïŒ";
continue;
}
$rowData = [];
for ($i = 0; $i < count($this->headers); $i++) {
$rowData[$this->headers[$i]] = isset($row[$i]) ? trim($row[$i]) : '';
}
// å¿
é é
ç®ã®æ€èšŒ
if (empty($rowData['ãµãŒãã¹å'])) {
$this->errors[] = "è¡{$rowNumber}: ãµãŒãã¹åã空ã§ã";
continue;
}
if (empty($rowData['è£
眮皮å¥'])) {
$this->errors[] = "è¡{$rowNumber}: è£
眮皮å¥ã空ã§ã";
continue;
}
if (empty($rowData['è£
眮åç§°'])) {
$this->errors[] = "è¡{$rowNumber}: è£
眮åç§°ã空ã§ã";
continue;
}
if (empty($rowData['ãŠãŒã¶å1'])) {
$this->errors[] = "è¡{$rowNumber}: ãŠãŒã¶å1ã空ã§ã";
continue;
}
$this->data[] = $rowData;
}
}
fclose($handle);
if (empty($this->data)) {
$this->errors[] = "æå¹ãªããŒã¿ãèŠã€ãããŸãã";
return false;
}
return empty($this->errors);
}
/**
* ããããŒæ
å ±ãååŸ
* @return array
*/
public function getHeaders() {
return $this->headers;
}
/**
* ããŒã¿ãååŸ
* @return array
*/
public function getData() {
return $this->data;
}
/**
* ãšã©ãŒæ
å ±ãååŸ
* @return array
*/
public function getErrors() {
return $this->errors;
}
/**
* device_infoããŒãã«ã®ã«ã©ã äžèЧãååŸ
* @return array
*/
public function getDeviceInfoColumns() {
return [
'ãµãŒãã¹å',
'è£
眮皮å¥',
'è£
眮åç§°',
'ãã°ã€ã³IP',
'ãŠãŒã¶å1',
'ãã¹ã¯ãŒã1',
'ãŠãŒã¶å2',
'ãã¹ã¯ãŒã2',
'ãŠãŒã¶å3',
'ãã¹ã¯ãŒã3',
'ãŠãŒã¶å4',
'ãã¹ã¯ãŒã4',
'ãŠãŒã¶å5',
'ãã¹ã¯ãŒã5',
'ãŠãŒã¶å6',
'ãã¹ã¯ãŒã6',
'ãŠãŒã¶å7',
'ãã¹ã¯ãŒã7',
'ãŠãŒã¶å8',
'ãã¹ã¯ãŒã8',
'ãŠãŒã¶å9',
'ãã¹ã¯ãŒã9',
'ãŠãŒã¶å10',
'ãã¹ã¯ãŒã10'
];
}
/**
* åºæ¬ã«ã©ã ïŒdevice_infoã«ç»é²ãããã«ã©ã ïŒãååŸ
* @return array
*/
public function getBasicColumns() {
return $this->getDeviceInfoColumns();
}
/**
* æ¡åŒµã«ã©ã ïŒdevice_info以å€ã®ã«ã©ã ïŒãååŸ
* @return array
*/
public function getExtendedColumns() {
$deviceInfoColumns = $this->getDeviceInfoColumns();
$extendedColumns = [];
foreach ($this->headers as $header) {
if (!in_array($header, $deviceInfoColumns)) {
$extendedColumns[] = $header;
}
}
return $extendedColumns;
}
/**
* äž»ããŒå€ãçæïŒããŒã¿çšïŒ
* @param array $rowData
* @return string
*/
public function generatePrimaryKey($rowData) {
return $rowData['ãµãŒãã¹å'] . '_' .
$rowData['è£
眮皮å¥'] . '_' .
$rowData['è£
眮åç§°'] . '_' .
$rowData['ãŠãŒã¶å1'];
}
/**
* äž»ããŒã«ã©ã åãçæïŒããŒãã«å®çŸ©çšïŒ
* @param array $rowData
* @return string
*/
public function generatePrimaryKeyColumnName($rowData) {
// äž»ããŒã«ã©ã åãå®å
šã«åºå®
return 'primary_key';
}
/**
* åçããŒãã«åãçæ
* @param array $rowData
* @return string
*/
public function generateTableName($rowData) {
return sanitizeTableName($rowData['ãµãŒãã¹å'] . '_' . $rowData['è£
眮皮å¥']);
}
/**
* ããŒã¿ãè£
眮æ
å ±ããŒãã«çšã«å€æ
* @param array $rowData
* @return array
*/
public function convertToDeviceInfo($rowData) {
$result = [
'primary_key' => $this->generatePrimaryKey($rowData),
'service_name' => $rowData['ãµãŒãã¹å'],
'device_type' => $rowData['è£
眮皮å¥'],
'device_name' => $rowData['è£
眮åç§°'],
'login_ip' => isset($rowData['ãã°ã€ã³IP']) ? $rowData['ãã°ã€ã³IP'] : null,
'username1' => $rowData['ãŠãŒã¶å1'],
'password1' => isset($rowData['ãã¹ã¯ãŒã1']) ? $rowData['ãã¹ã¯ãŒã1'] : null
];
// ãŠãŒã¶å2-10ããã¹ã¯ãŒã2-10ã远å
for ($i = 2; $i <= 10; $i++) {
$result["username{$i}"] = isset($rowData["ãŠãŒã¶å{$i}"]) ? $rowData["ãŠãŒã¶å{$i}"] : null;
$result["password{$i}"] = isset($rowData["ãã¹ã¯ãŒã{$i}"]) ? $rowData["ãã¹ã¯ãŒã{$i}"] : null;
}
return $result;
}
/**
* ããŒã¿ãåçããŒãã«çšã«å€æ
* @param array $rowData
* @return array
*/
public function convertToExtendedData($rowData) {
$extendedColumns = $this->getExtendedColumns();
// äž»ããŒã«ã©ã åã¯æ¥æ¬èªã®ãŸãŸäœ¿çš
$primaryKeyColumnName = $this->generatePrimaryKeyColumnName($rowData);
$primaryKeyValue = $this->generatePrimaryKey($rowData);
$result = [
$primaryKeyColumnName => $primaryKeyValue
];
foreach ($extendedColumns as $column) {
// æ¡åŒµã«ã©ã åãæ¥æ¬èªã®ãŸãŸäœ¿çš
$result[$column] = isset($rowData[$column]) ? $rowData[$column] : null;
}
return $result;
}
/**
* CSVãã¡ã€ã«ã®çµ±èšæ
å ±ãååŸ
* @return array
*/
public function getStatistics() {
if (empty($this->data)) {
return [
'total_rows' => 0,
'services' => [],
'device_types' => [],
'unique_combinations' => 0
];
}
$services = [];
$deviceTypes = [];
$combinations = [];
foreach ($this->data as $row) {
$services[] = $row['ãµãŒãã¹å'];
$deviceTypes[] = $row['è£
眮皮å¥'];
$combinations[] = $row['ãµãŒãã¹å'] . '_' . $row['è£
眮皮å¥'];
}
return [
'total_rows' => count($this->data),
'services' => array_unique($services),
'device_types' => array_unique($deviceTypes),
'unique_combinations' => array_unique($combinations)
];
}
/**
* ããªããŒã·ã§ã³ã®å®è¡
* @return bool
*/
public function validate() {
$this->errors = [];
if (empty($this->headers)) {
$this->errors[] = "CSVãã¡ã€ã«ãèªã¿èŸŒãŸããŠããŸãã";
return false;
}
if (empty($this->data)) {
$this->errors[] = "ããŒã¿ãååšããŸãã";
return false;
}
// äž»ããŒã®éè€ãã§ãã¯
$primaryKeys = [];
foreach ($this->data as $index => $row) {
$primaryKey = $this->generatePrimaryKey($row);
if (in_array($primaryKey, $primaryKeys)) {
$this->errors[] = "è¡" . ($index + 2) . ": äž»ããŒãéè€ããŠããŸã - " . $primaryKey;
} else {
$primaryKeys[] = $primaryKey;
}
}
// IPã¢ãã¬ã¹åœ¢åŒã®ãã§ãã¯ïŒååšããå Žåã®ã¿ïŒ
foreach ($this->data as $index => $row) {
if (!empty($row['ãã°ã€ã³IP']) && !filter_var($row['ãã°ã€ã³IP'], FILTER_VALIDATE_IP)) {
$this->errors[] = "è¡" . ($index + 2) . ": ãã°ã€ã³IPã®åœ¢åŒãäžæ£ã§ã - " . $row['ãã°ã€ã³IP'];
}
}
return empty($this->errors);
}
}
?>
<?php
require_once 'config.php';
require_once __DIR__ . '/includes/auth_helper.php';
// ãã°ã€ã³å¿
é
requireLogin();
$pageTitle = 'è£
眮æ
å ±ç®¡ç - è£
眮æ
å ±ç®¡çã·ã¹ãã ';
// åæããŒã¿ååŸçš
try {
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
$deviceManager = new DeviceManager($database);
} catch (Exception $e) {
setErrorMessage("ããŒã¿ããŒã¹ãšã©ãŒ: " . $e->getMessage());
}
$errorMessage = getErrorMessage();
$successMessage = getSuccessMessage();
// å
±éããããŒãèªã¿èŸŒã¿
require_once 'includes/header.php';
?>
<div class="main-content">
<div class="page-container">
<div class="page-header">
<h1 class="page-title">
<div class="page-title-icon black-svg">
<?php include 'svgs/info.svg'; ?>
</div>
è£
眮æ
å ±ç®¡ç
</h1>
</div>
<?php if ($errorMessage): ?>
<div class="alert alert-error">
<svg class="alert-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"/>
</svg>
<div>
<strong>ãšã©ãŒ:</strong> <?= h($errorMessage) ?>
</div>
</div>
<?php endif; ?>
<?php if ($successMessage): ?>
<div class="alert alert-success">
<svg class="alert-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z"/>
</svg>
<div>
<strong>æå:</strong> <?= h($successMessage) ?>
</div>
</div>
<?php endif; ?>
<!-- æ€çŽ¢ãã©ãŒã -->
<form id="searchForm" class="search-form">
<h3 class="form-section-title">
æ€çŽ¢æ¡ä»¶
</h3>
<div class="form-row">
<div class="form-group">
<label for="serviceName">ãµãŒãã¹å:</label>
<select id="serviceName" name="service_name">
<option value="">-- ãã¹ãŠã®ãµãŒãã¹ --</option>
</select>
</div>
<div class="form-group">
<label for="deviceType">è£
眮皮å¥:</label>
<select id="deviceType" name="device_type">
<option value="">-- ãã¹ãŠã®è£
çœ®çš®å¥ --</option>
</select>
</div>
<div class="form-group">
<label for="deviceName">è£
眮åç§°:</label>
<input type="text" id="deviceName" name="device_name" placeholder="éšåäžèŽã§æ€çŽ¢">
</div>
</div>
<div class="form-row">
<button type="submit" id="searchBtn" class="btn btn-primary">
<div class="btn-icon">
<?php include 'svgs/search.svg'; ?>
</div> æ€çŽ¢
</button>
</div>
</form>
<!-- æ€çŽ¢çµæ -->
<div id="searchResults" class="search-results">
<div class="results-header">
<div class="results-info" id="resultsInfo">æ€çŽ¢çµæ: 0ä»¶</div>
</div>
<div id="loadingIndicator" class="loading" style="display: none;">
<div class="spinner"></div>
<div>æ€çŽ¢äž...</div>
</div>
<div id="resultsContainer">
<table class="results-table" id="resultsTable">
<thead>
<tr>
<th>ãµãŒãã¹å</th>
<th>è£
眮皮å¥</th>
<th>è£
眮åç§°</th>
<th>ãã°ã€ã³IP</th>
<th>ãŠãŒã¶å</th>
<th style="display: none;">äœæè
</th>
<th style="display: none;">æŽæ°è
</th>
<th style="display: none;">ç»é²æ¥æ</th>
<th style="display: none;">æŽæ°æ¥æ</th>
<th>æäœ</th>
</tr>
</thead>
<tbody id="resultsTableBody">
</tbody>
</table>
</div>
<div class="pagination" id="paginationContainer">
</div>
</div>
</div>
<!-- ç·šéã¢ãŒãã« -->
<div id="editModal" class="modal" style="display: none;">
<div class="modal-content" style="max-width: 800px;">
<div class="modal-header">
<h2>è£
眮æ
å ±ç·šé</h2>
<span class="close" onclick="closeEditModal()">×</span>
</div>
<form id="editForm">
<input type="hidden" id="edit_primary_key" name="primary_key">
<input type="hidden" id="edit_old_service_name" name="old_service_name">
<input type="hidden" id="edit_old_device_type" name="old_device_type">
<!-- äœæè
ã»æŽæ°è
æ
å ± -->
<div style="background: #f8f9fa; padding: 15px; border-radius: 5px; margin-bottom: 20px;">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
<div>
<strong>äœæè
:</strong> <span id="display_created_by" style="color: #495057;">-</span>
</div>
<div>
<strong>æŽæ°è
:</strong> <span id="display_updated_by" style="color: #495057;">-</span>
</div>
</div>
</div>
<div class="form-group">
<label for="edit_service_name">ãµãŒãã¹å:<span style="color: red;">*</span></label>
<input type="text" id="edit_service_name" name="service_name" required>
</div>
<div class="form-group">
<label for="edit_device_type">è£
眮皮å¥:<span style="color: red;">*</span></label>
<input type="text" id="edit_device_type" name="device_type" required>
</div>
<div class="form-group">
<label for="edit_device_name">è£
眮åç§°:<span style="color: red;">*</span></label>
<input type="text" id="edit_device_name" name="device_name" required>
</div>
<div class="form-group">
<label for="edit_login_ip">ãã°ã€ã³IP:</label>
<input type="text" id="edit_login_ip" name="login_ip">
</div>
<h4 style="margin-top: 20px; margin-bottom: 10px;">èªèšŒæ
å ±</h4>
<div class="form-group">
<label for="edit_username1">ãŠãŒã¶å1:<span style="color: red;">*</span></label>
<input type="text" id="edit_username1" name="username1" required>
</div>
<div class="form-group">
<label for="edit_password1">ãã¹ã¯ãŒã1:</label>
<input type="password" id="edit_password1" name="password1">
</div>
<div id="additionalCredentials">
<!-- ãŠãŒã¶å2-10ããã¹ã¯ãŒã2-10 -->
</div>
<button type="button" onclick="toggleAdditionalCredentials()" style="margin-bottom: 20px; background: #6c757d;">
远å ã®èªèšŒæ
å ±ã衚瀺/é衚瀺
</button>
<!-- åçããŒãã«ã®æ¡åŒµåãã£ãŒã«ã -->
<div id="extendedFields">
<!-- åçããŒãã«ã®æ¡åŒµåãããã«è¿œå ãããŸã -->
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">ä¿å</button>
<button type="button" class="btn btn-secondary" onclick="closeEditModal()">ãã£ã³ã»ã«</button>
</div>
</form>
</div>
</div>
<script>
// å
±éfetch颿°ïŒã»ãã·ã§ã³Cookieéä¿¡ã®ãã credentials: 'same-origin' ãä»äžïŒ
async function apiFetch(url, options) {
return fetch(url, Object.assign({ credentials: 'same-origin' }, options));
}
// ã°ããŒãã«å€æ°
let currentPage = 1;
let currentSearchParams = {};
let allResults = [];
// ããŒãžèªã¿èŸŒã¿æã®åæå
document.addEventListener('DOMContentLoaded', function() {
loadServices();
searchDevices(); // åæè¡šç€º
// æ€çŽ¢ãã©ãŒã ã®ãµãããã
document.getElementById('searchForm').addEventListener('submit', function(e) {
e.preventDefault();
currentPage = 1;
searchDevices();
});
// ãµãŒãã¹å倿޿ã«è£
眮皮å¥ãæŽæ°
document.getElementById('serviceName').addEventListener('change', function() {
loadDeviceTypes(this.value);
});
// ç·šéãã©ãŒã ã®ãµãããã
document.getElementById('editForm').addEventListener('submit', function(e) {
e.preventDefault();
submitEdit();
});
// 远å ã®èªèšŒæ
å ±ãã£ãŒã«ããçæ
generateAdditionalCredentials();
});
// ãµãŒãã¹åäžèЧãèªã¿èŸŒã¿
async function loadServices() {
console.log('ð loadServices éå§');
try {
const response = await apiFetch('ajax_api.php?action=get_services');
console.log('ð¥ loadServices response status:', response.status);
const result = await response.json();
console.log('ð loadServices result:', result);
if (result.success) {
const select = document.getElementById('serviceName');
if (!select) {
console.error('â serviceName ã»ã¬ã¯ããèŠã€ãããŸãã');
return;
}
select.innerHTML = '<option value="">-- ãã¹ãŠã®ãµãŒãã¹ --</option>';
result.data.forEach(service => {
const option = document.createElement('option');
option.value = service;
option.textContent = service;
select.appendChild(option);
});
console.log('â
loadServices å®äº:', result.data.length, 'ä»¶');
} else {
console.error('â loadServices 倱æ:', result.message);
showAlert('error', 'ãµãŒãã¹åã®èªã¿èŸŒã¿ã«å€±æããŸãã: ' + result.message);
}
} catch (error) {
console.error('â Error loading services:', error);
showAlert('error', 'ãµãŒãã¹åã®èªã¿èŸŒã¿äžã«ãšã©ãŒãçºçããŸãã');
}
}
// è£
眮皮å¥äžèЧãèªã¿èŸŒã¿
async function loadDeviceTypes(serviceName) {
const deviceTypeSelect = document.getElementById('deviceType');
if (!serviceName) {
deviceTypeSelect.innerHTML = '<option value="">-- ãã¹ãŠã®è£
çœ®çš®å¥ --</option>';
return;
}
try {
deviceTypeSelect.disabled = true;
deviceTypeSelect.innerHTML = '<option value="">-- èªã¿èŸŒã¿äž... --</option>';
const response = await apiFetch(`ajax_api.php?action=get_device_types&service_name=${encodeURIComponent(serviceName)}`);
const result = await response.json();
if (result.success) {
deviceTypeSelect.innerHTML = '<option value="">-- ãã¹ãŠã®è£
çœ®çš®å¥ --</option>';
result.data.forEach(deviceType => {
const option = document.createElement('option');
option.value = deviceType;
option.textContent = deviceType;
deviceTypeSelect.appendChild(option);
});
deviceTypeSelect.disabled = false;
} else {
showAlert('error', 'è£
眮皮å¥ã®èªã¿èŸŒã¿ã«å€±æããŸãã: ' + result.message);
deviceTypeSelect.innerHTML = '<option value="">-- ãšã©ãŒ --</option>';
}
} catch (error) {
console.error('Error loading device types:', error);
showAlert('error', 'è£
眮皮å¥ã®èªã¿èŸŒã¿äžã«ãšã©ãŒãçºçããŸãã');
deviceTypeSelect.innerHTML = '<option value="">-- ãšã©ãŒ --</option>';
}
}
// è£
眮æ
å ±ãæ€çŽ¢
async function searchDevices(page = 1) {
const serviceName = document.getElementById('serviceName').value;
const deviceType = document.getElementById('deviceType').value;
const deviceName = document.getElementById('deviceName').value;
console.log('ð searchDevices éå§', { serviceName, deviceType, deviceName, page });
currentSearchParams = {
service_name: serviceName,
device_type: deviceType,
device_name: deviceName,
page: page
};
const searchResults = document.getElementById('searchResults');
const loadingIndicator = document.getElementById('loadingIndicator');
const resultsContainer = document.getElementById('resultsContainer');
// æ€çŽ¢çµæãšãªã¢å
šäœã衚瀺
searchResults.style.display = 'block';
loadingIndicator.style.display = 'flex';
resultsContainer.style.display = 'none';
try {
const params = new URLSearchParams(currentSearchParams);
params.append('action', 'search_devices');
const url = 'ajax_api.php?' + params.toString();
console.log('ð¡ Request URL:', url);
const response = await apiFetch(url);
console.log('ð¥ Response status:', response.status);
const text = await response.text();
console.log('ð Response text length:', text.length);
let result;
try {
result = JSON.parse(text);
console.log('â
JSON parse success:', result);
} catch (e) {
console.error('â JSON parse error:', e);
console.error('Response text:', text.substring(0, 500));
throw new Error('JSONããŒã¹ãšã©ãŒ: ' + e.message);
}
if (result.success) {
console.log('â
æ€çŽ¢æå:', result.data.pagination.total_count, 'ä»¶');
displayResults(result.data);
console.log('â
displayResults ããæ»ããŸãã');
currentPage = page;
} else {
console.error('â æ€çޢ倱æ:', result.message);
showAlert('error', 'æ€çŽ¢ã«å€±æããŸãã: ' + result.message);
}
} catch (error) {
console.error('â Error searching devices:', error);
showAlert('error', 'æ€çŽ¢äžã«ãšã©ãŒãçºçããŸãã: ' + error.message);
} finally {
console.log('ð finally ãããã¯: loadingIndicatoré衚瀺ãresultsContainer衚瀺');
loadingIndicator.style.display = 'none';
resultsContainer.style.display = 'block';
console.log('ð finallyåŸã®DOMç¶æ
:');
console.log(' searchResults.style.display:', searchResults.style.display);
console.log(' loadingIndicator.style.display:', loadingIndicator.style.display);
console.log(' resultsContainer.style.display:', resultsContainer.style.display);
console.log(' searchResults.offsetHeight:', searchResults.offsetHeight);
console.log(' resultsContainer.offsetHeight:', resultsContainer.offsetHeight);
console.log('ð searchDevices å®äº');
}
}
// æ€çŽ¢çµæã衚瀺
function displayResults(data) {
console.log('ð displayResults éå§', data);
console.log(' devices:', data.devices);
console.log(' devices[0]:', data.devices[0]);
const tableBody = document.getElementById('resultsTableBody');
const resultsInfo = document.getElementById('resultsInfo');
const paginationContainer = document.getElementById('paginationContainer');
if (!tableBody) {
console.error('â resultsTableBody ãèŠã€ãããŸãã');
return;
}
if (!resultsInfo) {
console.error('â resultsInfo ãèŠã€ãããŸãã');
return;
}
if (!paginationContainer) {
console.error('â paginationContainer ãèŠã€ãããŸãã');
return;
}
console.log('â
DOMèŠçŽ ãã¹ãŠååš');
resultsInfo.textContent = `æ€çŽ¢çµæ: ${data.pagination.total_count}ä»¶ïŒ${data.pagination.current_page}/${data.pagination.total_pages}ããŒãžïŒ`;
tableBody.innerHTML = '';
if (data.devices.length === 0) {
console.error('æ€çŽ¢çµæ0ä»¶');
tableBody.innerHTML = '<tr><td colspan="6" style="text-align: center; color: #6c757d;">æ€çŽ¢æ¡ä»¶ã«äžèŽããããŒã¿ãèŠã€ãããŸããã§ãã</td></tr>';
} else {
console.log('â
æ€çŽ¢çµæãã:', data.devices.length, 'ä»¶');
data.devices.forEach((device, index) => {
console.log(`ð ããã€ã¹${index + 1}åŠçéå§:`, device.device_name);
try {
const row = document.createElement('tr');
console.log(' â trèŠçŽ äœæ');
row.innerHTML = `
<td>${escapeHtml(device.service_name)}</td>
<td>${escapeHtml(device.device_type)}</td>
<td class="text-truncate" title="${escapeHtml(device.device_name)}">${escapeHtml(device.device_name)}</td>
<td>${escapeHtml(device.login_ip || '-')}</td>
<td>${escapeHtml(device.username1)}</td>
<td style="display: none;">${escapeHtml(device.created_by || '-')}</td>
<td style="display: none;">${escapeHtml(device.updated_by || '-')}</td>
<td style="display: none;">${formatDateTime(device.created_at)}</td>
<td style="display: none;">${formatDateTime(device.updated_at)}</td>
<td>
<button class="btn-macro" onclick="downloadTeratermMacro('${escapeHtml(device.login_ip || '')}', '${escapeHtml(device.username1)}', '${escapeHtml(device.password1 || '')}', '${escapeHtml(device.device_name)}')"
${!device.login_ip || !device.username1 || !device.password1 ? 'disabled title="IPã¢ãã¬ã¹ããŠãŒã¶åããã¹ã¯ãŒããå¿
èŠã§ã"' : ''}>
ãã¯ã
</button>
<button class="btn-edit" onclick="openEditModal('${escapeHtml(device.primary_key)}')">
ç·šé
</button>
<button class="btn-delete" onclick="confirmDelete('${escapeHtml(device.primary_key)}', '${escapeHtml(device.device_name)}', '${escapeHtml(device.service_name)}', '${escapeHtml(device.device_type)}')">
åé€
</button>
</td>
`;
console.log(' â innerHTMLèšå®');
tableBody.appendChild(row);
console.log(' â è¡è¿œå å®äº');
} catch (error) {
console.error(` â ããã€ã¹${index + 1}åŠçãšã©ãŒ:`, error);
}
});
console.log('â
forEachå®äº');
}
// ããŒãžããŒã·ã§ã³è¡šç€º
console.log('ð ããŒãžããŒã·ã§ã³è¡šç€ºéå§');
displayPagination(data.pagination);
// ãããã°: DOMç¶æ
確èª
console.log('ð DOMç¶æ
確èª:');
console.log(' tableBody.children.length:', tableBody.children.length);
console.log(' tableBody.innerHTML.length:', tableBody.innerHTML.length);
console.log(' resultsContainer.style.display:', resultsContainer.style.display);
console.log(' resultsContainer.offsetHeight:', resultsContainer.offsetHeight);
console.log(' resultsContainer.offsetWidth:', resultsContainer.offsetWidth);
console.log('â
displayResults å®äº');
}
// ããŒãžããŒã·ã§ã³è¡šç€º
function displayPagination(pagination) {
console.log('ð displayPagination éå§:', pagination);
const container = document.getElementById('paginationContainer');
if (pagination.total_pages <= 1) {
container.innerHTML = '';
return;
}
let html = '<div class="pagination-buttons">';
// åãžãã¿ã³
if (pagination.current_page > 1) {
html += `<button class="pagination-btn" onclick="searchDevices(${pagination.current_page - 1})">â åãž</button>`;
}
// ããŒãžçªå·
const startPage = Math.max(1, pagination.current_page - 2);
const endPage = Math.min(pagination.total_pages, pagination.current_page + 2);
for (let i = startPage; i <= endPage; i++) {
const activeClass = i === pagination.current_page ? 'active' : '';
html += `<button class="pagination-btn ${activeClass}" onclick="searchDevices(${i})">${i}</button>`;
}
// 次ãžãã¿ã³
if (pagination.current_page < pagination.total_pages) {
html += `<button class="pagination-btn" onclick="searchDevices(${pagination.current_page + 1})">次㞠â</button>`;
}
html += '</div>';
container.innerHTML = html;
console.log('â
displayPagination å®äº');
}
// ç·šéã¢ãŒãã«ãéã
async function openEditModal(primaryKey) {
try {
const response = await apiFetch(`ajax_api.php?action=get_device&primary_key=${encodeURIComponent(primaryKey)}`);
const result = await response.json();
if (result.success) {
const device = result.data.device;
const extendedData = result.data.extended_data || {};
const extendedColumns = result.data.extended_columns || [];
// ãã©ãŒã ã«å€ãã»ãã
document.getElementById('edit_primary_key').value = device.primary_key;
document.getElementById('edit_old_service_name').value = device.service_name;
document.getElementById('edit_old_device_type').value = device.device_type;
document.getElementById('edit_service_name').value = device.service_name;
document.getElementById('edit_device_type').value = device.device_type;
document.getElementById('edit_device_name').value = device.device_name;
document.getElementById('edit_login_ip').value = device.login_ip || '';
document.getElementById('edit_username1').value = device.username1;
document.getElementById('edit_password1').value = device.password1 || '';
// äœæè
ã»æŽæ°è
æ
å ±ã衚瀺
document.getElementById('display_created_by').textContent = device.created_by || '-';
document.getElementById('display_updated_by').textContent = device.updated_by || '-';
// 远å ã®èªèšŒæ
å ±
for (let i = 2; i <= 10; i++) {
document.getElementById(`edit_username${i}`).value = device[`username${i}`] || '';
document.getElementById(`edit_password${i}`).value = device[`password${i}`] || '';
}
// åçããŒãã«ã®æ¡åŒµåãã£ãŒã«ããçæ
const extendedFieldsContainer = document.getElementById('extendedFields');
if (extendedColumns.length > 0) {
let html = '<h4 style="margin-top: 20px; margin-bottom: 10px;">åçããŒãã«æ¡åŒµå</h4>';
extendedColumns.forEach(col => {
html += `
<div class="form-group">
<label for="extended_${escapeHtml(col)}">${escapeHtml(col)}:</label>
<input type="text" id="extended_${escapeHtml(col)}" name="extended_${escapeHtml(col)}" value="${escapeHtml(extendedData[col] || '')}">
</div>
`;
});
extendedFieldsContainer.innerHTML = html;
} else {
extendedFieldsContainer.innerHTML = '';
}
// ã¢ãŒãã«ã衚瀺
document.getElementById('editModal').style.display = 'flex';
} else {
showAlert('error', 'è£
眮æ
å ±ã®ååŸã«å€±æããŸãã: ' + result.message);
}
} catch (error) {
console.error('Error loading device:', error);
showAlert('error', 'è£
眮æ
å ±ã®ååŸäžã«ãšã©ãŒãçºçããŸãã');
}
}
// ç·šéã¢ãŒãã«ãéãã
function closeEditModal() {
document.getElementById('editModal').style.display = 'none';
}
// 远å ã®èªèšŒæ
å ±ãã£ãŒã«ããçæ
function generateAdditionalCredentials() {
const container = document.getElementById('additionalCredentials');
let html = '';
for (let i = 2; i <= 10; i++) {
html += `
<div class="form-group" style="display: none;" id="credentials_group_${i}">
<h5 style="margin-top: 15px;">èªèšŒæ
å ± ${i}</h5>
<label for="edit_username${i}">ãŠãŒã¶å${i}:</label>
<input type="text" id="edit_username${i}" name="username${i}">
<label for="edit_password${i}">ãã¹ã¯ãŒã${i}:</label>
<input type="password" id="edit_password${i}" name="password${i}">
</div>
`;
}
container.innerHTML = html;
}
// 远å ã®èªèšŒæ
å ±ã®è¡šç€º/é衚瀺åãæ¿ã
function toggleAdditionalCredentials() {
for (let i = 2; i <= 10; i++) {
const group = document.getElementById(`credentials_group_${i}`);
if (group.style.display === 'none') {
group.style.display = 'block';
} else {
group.style.display = 'none';
}
}
}
// ç·šéãéä¿¡
async function submitEdit() {
const formData = new FormData(document.getElementById('editForm'));
formData.append('action', 'update_device');
try {
const response = await apiFetch('ajax_api.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
showAlert('success', result.message);
closeEditModal();
searchDevices(currentPage); // çŸåšã®ããŒãžãåèªã¿èŸŒã¿
} else {
showAlert('error', 'æŽæ°ã«å€±æããŸãã: ' + result.message);
}
} catch (error) {
console.error('Error updating device:', error);
showAlert('error', 'æŽæ°äžã«ãšã©ãŒãçºçããŸãã');
}
}
// åé€ç¢ºèª
function confirmDelete(primaryKey, deviceName, serviceName, deviceType) {
if (confirm(`æ¬åœã«ã${deviceName}ããåé€ããŸããïŒ\nãã®æäœã¯åãæ¶ããŸããã`)) {
deleteDevice(primaryKey, serviceName, deviceType);
}
}
// åé€å®è¡
async function deleteDevice(primaryKey, serviceName, deviceType) {
const formData = new FormData();
formData.append('action', 'delete_device');
formData.append('primary_key', primaryKey);
formData.append('service_name', serviceName);
formData.append('device_type', deviceType);
try {
const response = await apiFetch('ajax_api.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
showAlert('success', result.message);
searchDevices(currentPage); // çŸåšã®ããŒãžãåèªã¿èŸŒã¿
} else {
showAlert('error', 'åé€ã«å€±æããŸãã: ' + result.message);
}
} catch (error) {
console.error('Error deleting device:', error);
showAlert('error', 'åé€äžã«ãšã©ãŒãçºçããŸãã');
}
}
// Teratermãã¯ãããŠã³ããŒã
async function downloadTeratermMacro(deviceIp, username, password, deviceName) {
if (!deviceIp || !username || !password) {
showAlert('error', 'IPã¢ãã¬ã¹ããŠãŒã¶åããã¹ã¯ãŒããå¿
èŠã§ã');
return;
}
try {
const params = new URLSearchParams({
action: 'generate_teraterm_macro',
device_ip: deviceIp,
username: username,
password: password,
device_name: deviceName
});
const response = await apiFetch('ajax_api.php?' + params.toString());
if (!response.ok) {
throw new Error('ãã¯ãã®çæã«å€±æããŸãã');
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${deviceName}_${deviceIp}.ttl`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
showAlert('success', 'Teratermãã¯ããããŠã³ããŒãããŸãã');
} catch (error) {
console.error('Macro download error:', error);
showAlert('error', 'ãã¯ãã®ããŠã³ããŒãã«å€±æããŸãã: ' + error.message);
}
}
// HTMLãšã¹ã±ãŒã
function escapeHtml(text) {
if (text === null || text === undefined) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// æ¥æãã©ãŒããã
function formatDateTime(datetime) {
if (!datetime) return '-';
const date = new Date(datetime);
return date.toLocaleString('ja-JP', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
// ã¢ã©ãŒã衚瀺
function showAlert(type, message) {
alert(message);
}
// ã¢ãŒãã«å€ã¯ãªãã¯ã§éãã
window.onclick = function(event) {
const modal = document.getElementById('editModal');
if (event.target == modal) {
closeEditModal();
}
}
</script>
<style>
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
align-items: center;
justify-content: center;
}
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 0;
border: 1px solid #888;
width: 90%;
max-width: 600px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.modal-header {
padding: 20px;
background-color: #2c3e50;
color: white;
border-radius: 8px 8px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h2 {
margin: 0;
}
.close {
color: white;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover,
.close:focus {
color: #ddd;
}
.modal-content form {
padding: 20px;
}
.modal-footer {
display: flex;
gap: 10px;
justify-content: flex-end;
padding-top: 20px;
border-top: 1px solid #dee2e6;
}
.btn-edit {
background-color: #007bff;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.btn-edit:hover {
background-color: #0056b3;
}
.btn-delete {
background-color: #dc3545;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.btn-delete:hover {
background-color: #c82333;
}
.btn-icon {
display: inline-flex;
align-items: center;
width: 16px;
height: 16px;
margin-right: 5px;
}
.btn-icon svg {
width: 100%;
height: 100%;
}
</style>
<?php require_once 'includes/footer.php'; ?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>éçãã¡ã€ã«ãã¹ã</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
.success { color: green; }
.error { color: red; }
.test-item { margin: 20px 0; padding: 10px; border: 1px solid #ccc; }
</style>
</head>
<body>
<h1>éçãã¡ã€ã«èªã¿èŸŒã¿ãã¹ã</h1>
<div class="test-item">
<h2>1. CSSèªã¿èŸŒã¿ãã¹ã</h2>
<link rel="stylesheet" href="css/styles.css">
<div id="cssTest">CSSãèªã¿èŸŒãŸããŠããŸãã</div>
<script>
const styles = window.getComputedStyle(document.body);
document.getElementById('cssTest').innerHTML =
styles.backgroundColor ?
'<span class="success">â
CSSãèªã¿èŸŒãŸããŠããŸã</span>' :
'<span class="error">â CSSãèªã¿èŸŒãŸããŠããŸãã</span>';
</script>
</div>
<div class="test-item">
<h2>2. SVGãã¡ã€ã«ãã¹ã</h2>
<p>SVGãã¡ã€ã«ã®èªã¿èŸŒã¿:</p>
<?php
$svgPath = __DIR__ . '/svgs/upload.svg';
if (file_exists($svgPath)) {
echo '<span class="success">â
SVGãã¡ã€ã«ãååšããŸã</span><br>';
echo '<div style="width:50px;height:50px;">';
include $svgPath;
echo '</div>';
} else {
echo '<span class="error">â SVGãã¡ã€ã«ãèŠã€ãããŸãã</span>';
}
?>
</div>
<div class="test-item">
<h2>3. ãã¡ã€ã«ããŒããã·ã§ã³</h2>
<?php
$dirs = ['css', 'svgs', 'uploads', 'logs', 'classes', 'includes'];
echo '<ul>';
foreach ($dirs as $dir) {
$path = __DIR__ . '/' . $dir;
if (file_exists($path)) {
$perms = substr(sprintf('%o', fileperms($path)), -4);
echo "<li><strong>$dir/</strong>: ååš (æš©é: $perms)</li>";
} else {
echo "<li><strong>$dir/</strong>: <span class='error'>ååšããŸãã</span></li>";
}
}
// CSSãã¡ã€ã«ã®ç¢ºèª
$cssFile = __DIR__ . '/css/styles.css';
if (file_exists($cssFile)) {
$perms = substr(sprintf('%o', fileperms($cssFile)), -4);
$readable = is_readable($cssFile) ? 'èªã¿åãå¯èœ' : 'èªã¿åãäžå¯';
echo "<li><strong>css/styles.css</strong>: ååš (æš©é: $perms, $readable)</li>";
} else {
echo "<li><strong>css/styles.css</strong>: <span class='error'>ååšããŸãã</span></li>";
}
echo '</ul>';
?>
</div>
<div class="test-item">
<h2>4. Apacheèšå®ç¢ºèª</h2>
<?php
if (function_exists('apache_get_modules')) {
$modules = apache_get_modules();
echo '<p>mod_rewrite: ' . (in_array('mod_rewrite', $modules) ?
'<span class="success">æå¹</span>' :
'<span class="error">ç¡å¹</span>') . '</p>';
} else {
echo '<p>Apache颿°ãå©çšã§ããŸãã</p>';
}
?>
<p>.htaccess: <?= file_exists(__DIR__ . '/.htaccess') ?
'<span class="success">ååšããŸã</span>' :
'<span class="error">ååšããŸãã</span>' ?></p>
</div>
<div class="test-item">
<h2>5. ããŒã¿ããŒã¹æ¥ç¶ãã¹ã</h2>
<?php
if (file_exists(__DIR__ . '/config.php')) {
require_once __DIR__ . '/config.php';
try {
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
$pdo = new PDO($dsn, DB_USER, DB_PASS);
echo '<span class="success">â
ããŒã¿ããŒã¹æ¥ç¶æå</span>';
echo '<br>ãã¹ã: ' . DB_HOST;
} catch (PDOException $e) {
echo '<span class="error">â ããŒã¿ããŒã¹æ¥ç¶ãšã©ãŒ: ' . htmlspecialchars($e->getMessage()) . '</span>';
echo '<br>ãã¹ã: ' . DB_HOST;
}
} else {
echo '<span class="error">â config.phpãèŠã€ãããŸãã</span>';
}
?>
</div>
<hr>
<p><a href="/">ãããããŒãžã«æ»ã</a> | <a href="debug.php">詳现ãããã°ããŒãž</a></p>
</body>
</html>
#!/bin/bash
set -e
# ServerNameèŠåãæå¶
echo "ServerName localhost" >> /etc/apache2/apache2.conf
# Renderã®ç°å¢å€æ°PORTã䜿çšããŠApacheãèšå®
if [ -n "$PORT" ]; then
echo "Configuring Apache to listen on port $PORT"
sed -i "s/Listen 80/Listen $PORT/g" /etc/apache2/ports.conf
sed -i "s/:80>/:$PORT>/g" /etc/apache2/sites-available/000-default.conf
else
echo "PORT environment variable not set, using default port 80"
fi
# Apacheèšå®ã衚瀺ïŒãããã°çšïŒ
echo "Apache will listen on:"
grep "Listen" /etc/apache2/ports.conf
# Apacheãèµ·å
exec apache2-foreground
#!/bin/bash
# MySQL ãªã¹ãã¢ã¹ã¯ãªãã
# äœ¿çšæ¹æ³: ./restore_mysql.sh <ããã¯ã¢ãããã¡ã€ã«ã®ãã¹>
# èšå®
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-3306}"
DB_NAME="${DB_NAME:-device_management}"
DB_USER="${DB_USER:-root}"
DB_PASS="${DB_PASS:-}"
# åŒæ°ãã§ãã¯
if [ $# -eq 0 ]; then
echo "äœ¿çšæ¹æ³: $0 <ããã¯ã¢ãããã¡ã€ã«ã®ãã¹>"
echo ""
echo "äŸ:"
echo " $0 backups/daily/backup_device_management_20260217_120000.sql.gz"
exit 1
fi
BACKUP_FILE="$1"
# ãã¡ã€ã«ã®ååšç¢ºèª
if [ ! -f "${BACKUP_FILE}" ]; then
echo "ãšã©ãŒ: ãã¡ã€ã«ãèŠã€ãããŸãã: ${BACKUP_FILE}"
exit 1
fi
echo "=========================================="
echo "ããŒã¿ããŒã¹ãªã¹ãã¢"
echo "ããã¯ã¢ãããã¡ã€ã«: ${BACKUP_FILE}"
echo "ããŒã¿ããŒã¹: ${DB_NAME}"
echo "=========================================="
echo ""
read -p "ãã®ããŒã¿ããŒã¹ã埩å
ããŸããïŒ çŸåšã®ããŒã¿ã¯äžæžããããŸãã(yes/no): " CONFIRM
if [ "${CONFIRM}" != "yes" ]; then
echo "ãªã¹ãã¢ããã£ã³ã»ã«ããŸããã"
exit 0
fi
echo ""
echo "ãªã¹ãã¢ãéå§ããŸã..."
# ãªã¹ãã¢ã®å®è¡
if [ -z "${DB_PASS}" ]; then
# ãã¹ã¯ãŒããªã
gunzip < "${BACKUP_FILE}" | mysql -h "${DB_HOST}" \
-P "${DB_PORT}" \
-u "${DB_USER}" \
"${DB_NAME}"
else
# ãã¹ã¯ãŒããã
gunzip < "${BACKUP_FILE}" | mysql -h "${DB_HOST}" \
-P "${DB_PORT}" \
-u "${DB_USER}" \
-p"${DB_PASS}" \
"${DB_NAME}"
fi
# ãªã¹ãã¢ã®æåŠã確èª
if [ $? -eq 0 ]; then
echo ""
echo "â ãªã¹ãã¢ãå®äºããŸãã"
echo "=========================================="
else
echo ""
echo "â ãšã©ãŒ: ãªã¹ãã¢ã«å€±æããŸãã"
echo "=========================================="
exit 1
fi
exit 0
<?php
require_once __DIR__ . '/config.php';
// æ¢ã«ãã°ã€ã³æžã¿ã®å Žåã¯ãªãã€ã¬ã¯ã
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_CHARSET, DB_TYPE, DB_PORT);
$user = new User($database);
if ($user->isLoggedIn()) {
header('Location: index.php');
exit;
}
$error = '';
$success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$confirmPassword = $_POST['confirm_password'] ?? '';
// CSRF察ç
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
$error = 'äžæ£ãªãªã¯ãšã¹ãã§ã';
} elseif ($password !== $confirmPassword) {
$error = 'ãã¹ã¯ãŒããäžèŽããŸãã';
} else {
$result = $user->register($username, $password);
if ($result['success']) {
$success = $result['message'];
// èªåãã°ã€ã³
$loginResult = $user->login($username, $password);
if ($loginResult['success']) {
header('Location: index.php');
exit;
}
} else {
$error = $result['error'];
}
}
}
// CSRFããŒã¯ã³çæ
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
include __DIR__ . '/includes/header.php';
?>
<style>
.register-container {
max-width: 450px;
margin: 50px auto;
padding: 30px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.register-container h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: bold;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.form-group input:focus {
outline: none;
border-color: #4CAF50;
}
.btn-register {
width: 100%;
padding: 12px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
.btn-register:hover {
background-color: #45a049;
}
.error-message {
background-color: #f44336;
color: white;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.success-message {
background-color: #4CAF50;
color: white;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.login-link a {
color: #4CAF50;
text-decoration: none;
}
.login-link a:hover {
text-decoration: underline;
}
.password-requirements {
font-size: 12px;
color: #666;
margin-top: 5px;
}
</style>
<div class="register-container">
<h2 style="display: flex; align-items: center; justify-content: center; gap: 10px;">
<span style="width: 28px; height: 28px; display: inline-flex;"><?php include 'svgs/register.svg'; ?></span>
ãŠãŒã¶ãŒç»é²
</h2>
<?php if ($error): ?>
<div class="error-message"><?php echo htmlspecialchars($error); ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="success-message"><?php echo htmlspecialchars($success); ?></div>
<?php endif; ?>
<form method="POST" action="register.php">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
<div class="form-group">
<label for="username">ãŠãŒã¶ãŒå</label>
<input type="text" id="username" name="username" required
value="<?php echo htmlspecialchars($_POST['username'] ?? ''); ?>"
minlength="3" maxlength="100">
<div class="password-requirements">â» 3æå以äžã§å
¥åããŠãã ãã</div>
</div>
<div class="form-group">
<label for="password">ãã¹ã¯ãŒã</label>
<input type="password" id="password" name="password" required minlength="6">
<div class="password-requirements">â» 6æå以äžã§å
¥åããŠãã ãã</div>
</div>
<div class="form-group">
<label for="confirm_password">ãã¹ã¯ãŒãïŒç¢ºèªïŒ</label>
<input type="password" id="confirm_password" name="confirm_password" required minlength="6">
</div>
<button type="submit" class="btn-register">ç»é²ãã</button>
</form>
<div class="login-link">
æ¢ã«ã¢ã«ãŠã³ãããæã¡ã®æ¹ã¯<a href="login.php">ãã¡ã</a>ãããã°ã€ã³
</div>
</div>
<?php include __DIR__ . '/includes/footer.php'; ?>
<?php
/**
* ãªã¬ãŒã·ã§ã³ããŒã¿åæ§ç¯ããŒã«
*/
require_once 'config.php';
require_once __DIR__ . '/includes/auth_helper.php';
// ãã°ã€ã³å¿
é
requireLogin();
header('Content-Type: text/html; charset=UTF-8');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ãªã¬ãŒã·ã§ã³åæ§ç¯</title>
<style>
body { font-family: sans-serif; margin: 20px; background: #f5f5f5; }
.section { background: white; padding: 20px; margin: 20px 0; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
h2 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
.success { color: green; }
.error { color: red; }
.warning { color: orange; }
table { width: 100%; border-collapse: collapse; margin: 10px 0; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background: #3498db; color: white; }
tr:nth-child(even) { background: #f9f9f9; }
.count { font-size: 24px; font-weight: bold; color: #3498db; }
.btn { display: inline-block; padding: 10px 20px; margin: 10px 5px; background: #3498db; color: white; text-decoration: none; border-radius: 4px; border: none; cursor: pointer; }
.btn:hover { background: #2980b9; }
</style>
</head>
<body>
<h1>ð ãªã¬ãŒã·ã§ã³ããŒã¿åæ§ç¯</h1>
<?php
try {
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
$deviceManager = new DeviceManager($database);
// POSTãªã¯ãšã¹ãã§å®è¡
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'rebuild') {
echo "<div class='section'>";
echo "<h2>å®è¡äž...</h2>";
try {
// ãªã¬ãŒã·ã§ã³ããŒãã«ã®ååšç¢ºèª
if (!$deviceManager->relationTableExists()) {
echo "<p class='warning'>â ãªã¬ãŒã·ã§ã³ããŒãã«ãååšããªãããäœæããŸã...</p>";
$deviceManager->createRelationTable();
echo "<p class='success'>â ãªã¬ãŒã·ã§ã³ããŒãã«ãäœæããŸãã</p>";
}
// æ¢åããŒã¿ãããªã¬ãŒã·ã§ã³ãæ§ç¯
$result = $deviceManager->buildRelationsFromExistingData();
echo "<h3>å®è¡çµæïŒ</h3>";
echo "<p>ç»é²ãããçµã¿åããæ°: <span class='count'>{$result['registered']}</span> / {$result['total_combinations']}</p>";
if ($result['registered'] > 0) {
echo "<p class='success'>â ãªã¬ãŒã·ã§ã³ããŒã¿ã®åæ§ç¯ãå®äºããŸãã</p>";
} else {
echo "<p class='warning'>â ç»é²ãããããŒã¿ããããŸãã</p>";
}
if (!empty($result['errors'])) {
echo "<h3>ãšã©ãŒïŒ</h3>";
echo "<ul>";
foreach ($result['errors'] as $error) {
echo "<li class='error'>" . htmlspecialchars($error) . "</li>";
}
echo "</ul>";
}
} catch (Exception $e) {
echo "<p class='error'>â ãšã©ãŒãçºçããŸãã: " . htmlspecialchars($e->getMessage()) . "</p>";
}
echo "<p><a href='rebuild_relations.php' class='btn'>åèªã¿èŸŒã¿</a></p>";
echo "<p><a href='debug_search.php' class='btn'>ãããã°ããŒã«ã§ç¢ºèª</a></p>";
echo "<p><a href='search.php' class='btn'>æ€çŽ¢ããŒãžãž</a></p>";
echo "</div>";
} else {
// GETãªã¯ãšã¹ãïŒçŸåšã®ç¶æ
ã衚瀺
echo "<div class='section'>";
echo "<h2>çŸåšã®ç¶æ
</h2>";
// device_infoã®ããŒã¿ç¢ºèª
if ($database->tableExists('device_info')) {
$stmt = $database->execute("SELECT COUNT(*) as count FROM device_info");
$result = $stmt->fetch();
$deviceCount = $result['count'];
echo "<p>device_info ã¬ã³ãŒãæ°: <span class='count'>{$deviceCount}</span> ä»¶</p>";
if ($deviceCount == 0) {
echo "<p class='warning'>â device_infoã«ããŒã¿ããããŸãããå
ã«CSVãã¢ããããŒãããŠãã ããã</p>";
echo "<p><a href='index.php' class='btn'>ãããããŒãžãž</a></p>";
echo "</div></body></html>";
exit;
}
// ãµãŒãã¹åã»è£
眮皮å¥ã®çµã¿åããã衚瀺
$stmt = $database->execute("
SELECT
service_name,
device_type,
COUNT(*) as device_count
FROM device_info
GROUP BY service_name, device_type
ORDER BY service_name, device_type
");
$combinations = $stmt->fetchAll();
echo "<h3>ç»é²ãããŠãããµãŒãã¹åã»è£
眮皮å¥ã®çµã¿åããïŒ</h3>";
echo "<table>";
echo "<tr><th>ãµãŒãã¹å</th><th>è£
眮皮å¥</th><th>è£
眮æ°</th></tr>";
foreach ($combinations as $combo) {
echo "<tr>";
echo "<td>" . htmlspecialchars($combo['service_name']) . "</td>";
echo "<td>" . htmlspecialchars($combo['device_type']) . "</td>";
echo "<td>" . htmlspecialchars($combo['device_count']) . "</td>";
echo "</tr>";
}
echo "</table>";
} else {
echo "<p class='error'>â device_infoããŒãã«ãååšããŸãã</p>";
echo "</div></body></html>";
exit;
}
echo "</div>";
// ãªã¬ãŒã·ã§ã³ããŒãã«ã®ç¶æ
echo "<div class='section'>";
echo "<h2>ãªã¬ãŒã·ã§ã³ããŒãã«ã®ç¶æ
</h2>";
if ($deviceManager->relationTableExists()) {
$stmt = $database->execute("SELECT COUNT(*) as count FROM service_device_type_relations WHERE is_active = 1");
$result = $stmt->fetch();
$relationCount = $result['count'];
echo "<p>æå¹ãªãªã¬ãŒã·ã§ã³æ°: <span class='count'>{$relationCount}</span> ä»¶</p>";
if ($relationCount == 0) {
echo "<p class='warning'>â ãªã¬ãŒã·ã§ã³ããŒã¿ãç»é²ãããŠããŸãã</p>";
} else {
// ãªã¬ãŒã·ã§ã³äžèЧã衚瀺
$stmt = $database->execute("
SELECT service_name, device_type, description, created_at
FROM service_device_type_relations
WHERE is_active = 1
ORDER BY service_name, device_type
");
$relations = $stmt->fetchAll();
echo "<h3>ç»é²æžã¿ãªã¬ãŒã·ã§ã³ïŒ</h3>";
echo "<table>";
echo "<tr><th>ãµãŒãã¹å</th><th>è£
眮皮å¥</th><th>説æ</th><th>ç»é²æ¥æ</th></tr>";
foreach ($relations as $rel) {
echo "<tr>";
echo "<td>" . htmlspecialchars($rel['service_name']) . "</td>";
echo "<td>" . htmlspecialchars($rel['device_type']) . "</td>";
echo "<td>" . htmlspecialchars($rel['description'] ?? '-') . "</td>";
echo "<td>" . htmlspecialchars($rel['created_at']) . "</td>";
echo "</tr>";
}
echo "</table>";
}
} else {
echo "<p class='error'>â ãªã¬ãŒã·ã§ã³ããŒãã«ãååšããŸãã</p>";
}
echo "</div>";
// å®è¡ãã¿ã³
echo "<div class='section'>";
echo "<h2>å®è¡</h2>";
echo "<p>æ¢åã® device_info ããŒã¿ãã service_device_type_relations ãèªåæ§ç¯ããŸãã</p>";
echo "<p>æ¢ã«ç»é²æžã¿ã®ãªã¬ãŒã·ã§ã³ã¯æŽæ°ãããŸãã</p>";
echo "<form method='POST'>";
echo "<input type='hidden' name='action' value='rebuild'>";
echo "<button type='submit' class='btn' style='font-size: 18px; padding: 15px 30px;'>ð ãªã¬ãŒã·ã§ã³ãåæ§ç¯ãã</button>";
echo "</form>";
echo "</div>";
}
$database->close();
} catch (Exception $e) {
echo "<div class='section'>";
echo "<h2 class='error'>â ãšã©ãŒ</h2>";
echo "<p class='error'>" . htmlspecialchars($e->getMessage()) . "</p>";
echo "</div>";
}
?>
<div class="section">
<h2>ð é¢é£ãªã³ã¯</h2>
<ul>
<li><a href="debug_search.php">ãããã°ããŒã«</a></li>
<li><a href="search.php">æ€çŽ¢ããŒãž</a></li>
<li><a href="manage.php">管çããŒãž</a></li>
<li><a href="index.php">ãããããŒãž</a></li>
</ul>
</div>
</body>
</html>
#!/bin/bash
# RenderããŒã¿ããŒã¹åæåã¹ã¯ãªãã
#
# äœ¿çšæ¹æ³:
# 1. Renderããã·ã¥ããŒãã§ããŒã¿ããŒã¹ã®æ¥ç¶æ
å ±ãååŸ
# 2. 以äžã®å€æ°ãèšå®
# 3. ./init-render-db.sh ãå®è¡
# ããŒã¿ããŒã¹æ¥ç¶æ
å ±ïŒRenderããã·ã¥ããŒãããååŸïŒ
DB_HOST="your-database-host.render.com"
DB_PORT="3306"
DB_NAME="device_management"
DB_USER="admin"
DB_PASS="your-database-password"
echo "RenderããŒã¿ããŒã¹ã«ã¹ããŒããé©çšããŸã..."
echo "æ¥ç¶å
: $DB_HOST:$DB_PORT"
echo "ããŒã¿ããŒã¹: $DB_NAME"
echo ""
# ã¹ããŒããã¡ã€ã«ã®ååšç¢ºèª
if [ ! -f "database/schema.sql" ]; then
echo "ãšã©ãŒ: database/schema.sql ãèŠã€ãããŸãã"
exit 1
fi
# MySQLã¯ã©ã€ã¢ã³ãã®ç¢ºèª
if ! command -v mysql &> /dev/null; then
echo "ãšã©ãŒ: MySQLã¯ã©ã€ã¢ã³ããã€ã³ã¹ããŒã«ãããŠããŸãã"
echo "ã€ã³ã¹ããŒã«æ¹æ³:"
echo " macOS: brew install mysql-client"
echo " Ubuntu: sudo apt-get install mysql-client"
echo " Windows: MySQLå
¬åŒãµã€ãããããŠã³ããŒã"
exit 1
fi
# ããŒã¿ããŒã¹ã«æ¥ç¶ããŠã¹ããŒããé©çš
echo "ã¹ããŒããé©çšäž..."
mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" < database/schema.sql
if [ $? -eq 0 ]; then
echo ""
echo "â
ã¹ããŒãã®é©çšãå®äºããŸããïŒ"
echo ""
echo "次ã®ã¹ããã:"
echo "1. Renderã®webãµãŒãã¹ãèµ·åããŠããããšã確èª"
echo "2. æäŸãããURLã«ã¢ã¯ã»ã¹"
echo "3. CSVãã¡ã€ã«ãã¢ããããŒãããŠãã¹ã"
else
echo ""
echo "â ãšã©ãŒãçºçããŸãã"
echo "æ¥ç¶æ
å ±ã確èªããŠãã ãã"
exit 1
fi
<?php
require_once 'config.php';
try {
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
echo "ãããŒããŒã¿ç»é²ãéå§ããŸã...\n\n";
// ãµãŒãã¹åãšè£
眮皮å¥ã®çµã¿åãã
$serviceName = 'ãã¹ããµãŒãã¹';
$deviceType = 'ã«ãŒã¿';
// ãªã¬ãŒã·ã§ã³ããŒãã«ã«ç»é²ïŒååšããªãå ŽåïŒ
$checkRelationSql = "SELECT COUNT(*) FROM service_device_type_relations WHERE service_name = ? AND device_type = ?";
$stmt = $database->execute($checkRelationSql, [$serviceName, $deviceType]);
$exists = $stmt->fetchColumn();
if ($exists == 0) {
$insertRelationSql = "INSERT INTO service_device_type_relations (service_name, device_type, description) VALUES (?, ?, ?)";
$database->execute($insertRelationSql, [$serviceName, $deviceType, 'ãã¹ãçšãããŒããŒã¿']);
echo "ãªã¬ãŒã·ã§ã³ããŒãã«ã«ç»é²ããŸãã: {$serviceName} - {$deviceType}\n";
}
// åçããŒãã«åãçæ
$tableName = sanitizeTableName($serviceName . '_' . $deviceType);
// åçããŒãã«ãååšããªãå Žåã¯äœæ
$checkTableSql = "SHOW TABLES LIKE '{$tableName}'";
$stmt = $database->execute($checkTableSql);
$tableExists = $stmt->rowCount() > 0;
if (!$tableExists) {
$createTableSql = "
CREATE TABLE `{$tableName}` (
primary_key VARCHAR(500) NOT NULL PRIMARY KEY,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
";
$database->execute($createTableSql);
echo "åçããŒãã«ãäœæããŸãã: {$tableName}\n";
}
// 10ä»¶ã®ãããŒããŒã¿ãç»é²
$dummyDevices = [
['device_name' => 'ã«ãŒã¿-001', 'login_ip' => '192.168.1.1', 'username' => 'admin', 'password' => 'pass1234'],
['device_name' => 'ã«ãŒã¿-002', 'login_ip' => '192.168.1.2', 'username' => 'admin', 'password' => 'pass5678'],
['device_name' => 'ã«ãŒã¿-003', 'login_ip' => '192.168.1.3', 'username' => 'root', 'password' => 'rootpass'],
['device_name' => 'ã«ãŒã¿-004', 'login_ip' => '192.168.1.4', 'username' => 'admin', 'password' => 'admin123'],
['device_name' => 'ã«ãŒã¿-005', 'login_ip' => '192.168.1.5', 'username' => 'operator', 'password' => 'oper456'],
['device_name' => 'ã«ãŒã¿-006', 'login_ip' => '192.168.1.6', 'username' => 'admin', 'password' => 'secure789'],
['device_name' => 'ã«ãŒã¿-007', 'login_ip' => '192.168.1.7', 'username' => 'admin', 'password' => 'test1111'],
['device_name' => 'ã«ãŒã¿-008', 'login_ip' => '192.168.1.8', 'username' => 'netadmin', 'password' => 'net2222'],
['device_name' => 'ã«ãŒã¿-009', 'login_ip' => '192.168.1.9', 'username' => 'admin', 'password' => 'pass3333'],
['device_name' => 'ã«ãŒã¿-010', 'login_ip' => '192.168.1.10', 'username' => 'admin', 'password' => 'pass4444'],
];
$insertedCount = 0;
$errorCount = 0;
foreach ($dummyDevices as $device) {
// primary_key ãçæ
$primaryKey = "{$serviceName}_{$deviceType}_{$device['device_name']}_{$device['username']}";
try {
// device_info ããŒãã«ã«æ¿å
¥
$insertDeviceSql = "
INSERT INTO device_info (
primary_key, service_name, device_type, device_name,
login_ip, username1, password1,
created_by, updated_by
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
login_ip = VALUES(login_ip),
password1 = VALUES(password1),
updated_by = VALUES(updated_by),
updated_at = CURRENT_TIMESTAMP
";
$database->execute($insertDeviceSql, [
$primaryKey,
$serviceName,
$deviceType,
$device['device_name'],
$device['login_ip'],
$device['username'],
$device['password'],
'ã·ã¹ãã 管çè
',
'ã·ã¹ãã 管çè
'
]);
// åçããŒãã«ã«ãæ¿å
¥
$insertDynamicSql = "
INSERT INTO `{$tableName}` (primary_key)
VALUES (?)
ON DUPLICATE KEY UPDATE updated_at = CURRENT_TIMESTAMP
";
$database->execute($insertDynamicSql, [$primaryKey]);
$insertedCount++;
echo "â ç»é²æå: {$device['device_name']} ({$device['login_ip']})\n";
} catch (Exception $e) {
$errorCount++;
echo "â ç»é²å€±æ: {$device['device_name']} - {$e->getMessage()}\n";
}
}
echo "\n" . str_repeat("=", 50) . "\n";
echo "ãããŒããŒã¿ç»é²å®äº\n";
echo "æå: {$insertedCount}ä»¶\n";
echo "倱æ: {$errorCount}ä»¶\n";
echo str_repeat("=", 50) . "\n";
} catch (Exception $e) {
echo "ãšã©ãŒãçºçããŸãã: " . $e->getMessage() . "\n";
exit(1);
}
<?php
require_once __DIR__ . '/config.php';
// æ¢ã«ãã°ã€ã³æžã¿ã®å Žåã¯ãªãã€ã¬ã¯ã
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_CHARSET, DB_TYPE, DB_PORT);
$user = new User($database);
if ($user->isLoggedIn()) {
header('Location: index.php');
exit;
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
// CSRF察ç
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
$error = 'äžæ£ãªãªã¯ãšã¹ãã§ã';
} else {
$result = $user->login($username, $password);
if ($result['success']) {
// ãªãã€ã¬ã¯ãå
ãæå®ïŒå
ã®ããŒãžã«æ»ãïŒ
$redirect = $_GET['redirect'] ?? 'index.php';
header('Location: ' . $redirect);
exit;
} else {
$error = $result['error'];
}
}
}
// CSRFããŒã¯ã³çæ
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
include __DIR__ . '/includes/header.php';
?>
<style>
.login-container {
max-width: 400px;
margin: 50px auto;
padding: 30px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.login-container h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: bold;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.form-group input:focus {
outline: none;
border-color: #4CAF50;
}
.btn-login {
width: 100%;
padding: 12px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
.btn-login:hover {
background-color: #45a049;
}
.error-message {
background-color: #f44336;
color: white;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
</style>
<div class="login-container">
<h2 style="display: flex; align-items: center; justify-content: center; gap: 10px;">
<span style="width: 28px; height: 28px; display: inline-flex;"><?php include 'svgs/login.svg'; ?></span>
ãã°ã€ã³
</h2>
<?php if ($error): ?>
<div class="error-message"><?php echo htmlspecialchars($error); ?></div>
<?php endif; ?>
<form method="POST" action="login.php<?php echo isset($_GET['redirect']) ? '?redirect=' . urlencode($_GET['redirect']) : ''; ?>">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
<div class="form-group">
<label for="username">ãŠãŒã¶ãŒå</label>
<input type="text" id="username" name="username" required
value="<?php echo htmlspecialchars($_POST['username'] ?? ''); ?>">
</div>
<div class="form-group">
<label for="password">ãã¹ã¯ãŒã</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit" class="btn-login">ãã°ã€ã³</button>
</form>
</div>
<?php include __DIR__ . '/includes/footer.php'; ?>
<?php
require_once __DIR__ . '/config.php';
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_CHARSET, DB_TYPE, DB_PORT);
$user = new User($database);
// ãã°ã¢ãŠãåŠç
$user->logout();
// ãã°ã€ã³ããŒãžã«ãªãã€ã¬ã¯ã
header('Location: login.php');
exit;
<?php
/**
* device_info ããŒãã«ãæ§ã¹ããŒãããæ°ã¹ããŒããžç§»è¡
*/
require_once 'config.php';
require_once __DIR__ . '/includes/auth_helper.php';
// ãã°ã€ã³å¿
é
requireLogin();
// 管çè
æš©éãã§ãã¯ïŒå¿
èŠã«å¿ããŠïŒ
// if (!isAdmin()) {
// die('管çè
æš©éãå¿
èŠã§ã');
// }
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>device_info ãã€ã°ã¬ãŒã·ã§ã³</title>
<style>
body { font-family: sans-serif; margin: 40px; }
.success { color: green; }
.error { color: red; }
.warning { color: orange; }
.log { background: #f5f5f5; padding: 10px; margin: 10px 0; border-left: 3px solid #333; }
</style>
</head>
<body>
<h1>device_info ããŒãã«ãã€ã°ã¬ãŒã·ã§ã³</h1>
<?php
try {
if (!$database->tableExists('device_info')) {
echo "<p class='warning'>device_info ããŒãã«ãååšããŸããããã€ã°ã¬ãŒã·ã§ã³äžèŠã§ãã</p>";
exit;
}
echo "<div class='log'>";
echo "<h3>ã¹ããã1: çŸåšã®ã¹ããŒã確èª</h3>";
$columns = $database->getTableColumns('device_info');
$existingColumnNames = array_column($columns, 'COLUMN_NAME');
echo "<p>æ¢åã«ã©ã æ°: " . count($existingColumnNames) . "</p>";
// æ§ã«ã©ã ã®ååšç¢ºèª
$hasOldColumns = in_array('device_ip', $existingColumnNames) || in_array('username', $existingColumnNames);
$hasNewColumns = in_array('login_ip', $existingColumnNames) && in_array('username1', $existingColumnNames);
if (!$hasOldColumns && $hasNewColumns) {
echo "<p class='success'>â æ¢ã«æ°ã¹ããŒãã§ãããã€ã°ã¬ãŒã·ã§ã³äžèŠã§ãã</p>";
echo "</div>";
exit;
}
if (!$hasOldColumns) {
echo "<p class='error'>â æ§ã¹ããŒãã§ã¯ãããŸãããæåã§ã¹ããŒãã確èªããŠãã ããã</p>";
echo "</div>";
exit;
}
echo "<p class='warning'>â æ§ã¹ããŒããæ€åºããŸããããã€ã°ã¬ãŒã·ã§ã³ãå®è¡ããŸã...</p>";
echo "</div>";
// ããŒã¿ä»¶æ°ç¢ºèª
$stmt = $database->execute("SELECT COUNT(*) as count FROM device_info");
$result = $stmt->fetch();
$dataCount = $result['count'];
echo "<div class='log'>";
echo "<h3>ã¹ããã2: æ¢åããŒã¿ç¢ºèª</h3>";
echo "<p>æ¢åããŒã¿ä»¶æ°: {$dataCount} ä»¶</p>";
echo "</div>";
// ããã¯ã¢ããããŒãã«äœæ
echo "<div class='log'>";
echo "<h3>ã¹ããã3: ããã¯ã¢ããããŒãã«äœæ</h3>";
$backupTableName = 'device_info_backup_' . date('Ymd_His');
if ($dbType === 'pgsql') {
$database->execute("CREATE TABLE \"{$backupTableName}\" AS SELECT * FROM device_info");
} else {
$database->execute("CREATE TABLE `{$backupTableName}` LIKE device_info");
$database->execute("INSERT INTO `{$backupTableName}` SELECT * FROM device_info");
}
echo "<p class='success'>â ããã¯ã¢ããããŒãã«äœæ: {$backupTableName}</p>";
echo "</div>";
// ãã©ã³ã¶ã¯ã·ã§ã³éå§
$database->beginTransaction();
try {
echo "<div class='log'>";
echo "<h3>ã¹ããã4: ã«ã©ã å倿Ž</h3>";
// device_ip â login_ip
if (in_array('device_ip', $existingColumnNames)) {
if ($dbType === 'pgsql') {
$database->execute("ALTER TABLE device_info RENAME COLUMN device_ip TO login_ip");
} else {
$database->execute("ALTER TABLE device_info CHANGE device_ip login_ip VARCHAR(45)");
}
echo "<p class='success'>â device_ip â login_ip</p>";
}
// username â username1
if (in_array('username', $existingColumnNames)) {
if ($dbType === 'pgsql') {
$database->execute("ALTER TABLE device_info RENAME COLUMN username TO username1");
} else {
$database->execute("ALTER TABLE device_info CHANGE username username1 VARCHAR(100) NOT NULL");
}
echo "<p class='success'>â username â username1</p>";
}
// password â password1
if (in_array('password', $existingColumnNames)) {
if ($dbType === 'pgsql') {
$database->execute("ALTER TABLE device_info RENAME COLUMN password TO password1");
} else {
$database->execute("ALTER TABLE device_info CHANGE password password1 VARCHAR(255)");
}
echo "<p class='success'>â password â password1</p>";
}
echo "</div>";
// 远å ã«ã©ã ã®äœæ
echo "<div class='log'>";
echo "<h3>ã¹ããã5: 远å ã«ã©ã äœæ</h3>";
$additionalColumns = [];
for ($i = 2; $i <= 10; $i++) {
if (!in_array("username{$i}", $existingColumnNames)) {
$additionalColumns[] = "username{$i}";
}
if (!in_array("password{$i}", $existingColumnNames)) {
$additionalColumns[] = "password{$i}";
}
}
foreach ($additionalColumns as $col) {
if ($dbType === 'pgsql') {
$database->execute("ALTER TABLE device_info ADD COLUMN \"{$col}\" VARCHAR(255)");
} else {
$database->execute("ALTER TABLE device_info ADD COLUMN `{$col}` VARCHAR(255)");
}
echo "<p class='success'>â ã«ã©ã 远å : {$col}</p>";
}
if (empty($additionalColumns)) {
echo "<p>远å ã«ã©ã ãªã</p>";
}
echo "</div>";
// ã€ã³ããã¯ã¹ã®åäœæ
echo "<div class='log'>";
echo "<h3>ã¹ããã6: ã€ã³ããã¯ã¹åäœæ</h3>";
try {
if ($dbType === 'pgsql') {
$database->execute("DROP INDEX IF EXISTS idx_device_info");
$database->execute("CREATE INDEX idx_device_info ON device_info (service_name, device_type, device_name, username1)");
} else {
$database->execute("DROP INDEX idx_device_info ON device_info");
$database->execute("CREATE INDEX idx_device_info ON device_info (service_name, device_type, device_name, username1)");
}
echo "<p class='success'>â ã€ã³ããã¯ã¹åäœæå®äº</p>";
} catch (Exception $e) {
echo "<p class='warning'>â ã€ã³ããã¯ã¹åäœæ: " . $e->getMessage() . "</p>";
}
echo "</div>";
// ã³ããã
$database->commit();
echo "<div class='log'>";
echo "<h2 class='success'>â ãã€ã°ã¬ãŒã·ã§ã³å®äº</h2>";
echo "<p>å
šãŠã®å€æŽãæ£åžžã«å®äºããŸããã</p>";
echo "<p>ããã¯ã¢ãã: {$backupTableName}</p>";
echo "<p><a href='check_db_schema.php'>â ã¹ããŒã確èª</a></p>";
echo "<p><a href='index.php'>â ãããããŒãžãžæ»ã</a></p>";
echo "</div>";
} catch (Exception $e) {
$database->rollBack();
echo "<p class='error'>â ãšã©ãŒãçºçããŸãã: " . htmlspecialchars($e->getMessage()) . "</p>";
echo "<p>倿Žã¯ããŒã«ããã¯ãããŸããã</p>";
echo "<p>ããã¯ã¢ããããŒãã« {$backupTableName} ããæåã§åŸ©å
ã§ããŸãã</p>";
}
} catch (Exception $e) {
echo "<p class='error'>â èŽåœçãšã©ãŒ: " . htmlspecialchars($e->getMessage()) . "</p>";
} finally {
$database->close();
}
?>
</body>
</html>
<?php
require_once 'config.php';
require_once __DIR__ . '/includes/auth_helper.php';
$pageTitle = 'è£
眮æ
å ±ç®¡çã·ã¹ãã ';
// ãã°ã€ã³ããŠããå Žå㯠upload.php ã«ãªãã€ã¬ã¯ã
if (isLoggedIn()) {
header('Location: upload.php');
exit;
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $pageTitle ?></title>
<link rel="stylesheet" href="css/styles.css">
<style>
.landing-page {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
.landing-container {
background: white;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
padding: 50px;
max-width: 700px;
width: 100%;
text-align: center;
}
.landing-logo {
width: 100px;
height: 100px;
margin: 0 auto 30px;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.landing-logo svg {
width: 60px;
height: 60px;
fill: white;
}
.landing-title {
font-size: 2.5em;
color: #333;
margin-bottom: 20px;
font-weight: bold;
}
.landing-subtitle {
font-size: 1.2em;
color: #666;
margin-bottom: 40px;
}
.info-section {
background: #f8f9fa;
border-radius: 8px;
padding: 30px;
margin-bottom: 30px;
text-align: left;
}
.info-section h2 {
color: #667eea;
font-size: 1.5em;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.info-section h2 svg {
width: 24px;
height: 24px;
fill: #667eea;
}
.info-list {
list-style: none;
padding: 0;
margin: 0;
}
.info-list li {
padding: 12px 0;
border-bottom: 1px solid #e0e0e0;
display: flex;
align-items: center;
gap: 12px;
font-size: 1.05em;
color: #444;
}
.info-list li:last-child {
border-bottom: none;
}
.info-list li svg {
width: 20px;
height: 20px;
fill: #667eea;
flex-shrink: 0;
}
.action-buttons {
display: flex;
gap: 20px;
justify-content: center;
margin-top: 30px;
}
.btn-primary {
color: white;
padding: 15px 40px;
border-radius: 8px;
text-decoration: none;
font-size: 1.1em;
font-weight: bold;
transition: transform 0.2s, box-shadow 0.2s;
display: inline-block;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: white;
color: #667eea;
padding: 15px 40px;
border: 2px solid #667eea;
border-radius: 8px;
text-decoration: none;
font-size: 1.1em;
font-weight: bold;
transition: all 0.2s;
display: inline-block;
}
.btn-secondary:hover {
background: #667eea;
color: white;
transform: translateY(-2px);
}
.update-date {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e0e0e0;
color: #999;
font-size: 0.9em;
}
@media (max-width: 768px) {
.landing-container {
padding: 30px 20px;
}
.landing-title {
font-size: 2em;
}
.action-buttons {
flex-direction: column;
}
.btn-primary, .btn-secondary {
width: 100%;
}
}
</style>
</head>
<body>
<div class="landing-page">
<div class="landing-container">
<div class="landing-logo">
<svg viewBox="0 0 24 24">
<path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,7H13V9H11V7M11,11H13V17H11V11Z"/>
</svg>
</div>
<h1 class="landing-title">è£
眮æ
å ±ç®¡çã·ã¹ãã </h1>
<p class="landing-subtitle">Device Information Management System</p>
<div class="info-section">
<h2>
<svg viewBox="0 0 24 24">
<path d="M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"/>
</svg>
ãå©çšã«ã€ããŠ
</h2>
<ul class="info-list">
<li>
<svg viewBox="0 0 24 24">
<path d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z"/>
</svg>
<strong>ãã°ã€ã³ãå¿
èŠã§ãïŒ</strong> ãã¹ãŠã®æ©èœã䜿çšããã«ã¯ãã°ã€ã³ãå¿
èŠã§ã
</li>
<li>
<svg viewBox="0 0 24 24">
<path d="M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z"/>
</svg>
<strong>ã¢ã«ãŠã³ãäœæå¯èœïŒ</strong> ã©ãªãã§ãèªç±ã«ã¢ã«ãŠã³ããäœæã§ããŸã
</li>
<li>
<svg viewBox="0 0 24 24">
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20M10,19L12,15H9V10H13V12L11,16H14V19H10Z"/>
</svg>
<strong>CSVãã¡ã€ã«ç®¡çïŒ</strong> è£
眮æ
å ±ãCSVã§äžæ¬ç®¡çã§ããŸã
</li>
<li>
<svg viewBox="0 0 24 24">
<path d="M19,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10L12,6H19A2,2 0 0,1 21,8H21L4,8V18L6.14,10H23.21L20.93,18.5C20.7,19.37 19.92,20 19,20Z"/>
</svg>
<strong>Teratermãã¯ãçæïŒ</strong> SSHæ¥ç¶çšã®ãã¯ããèªåçæããŸã
</li>
</ul>
</div>
<div class="action-buttons">
<a href="login.php" class="btn-primary">ãã°ã€ã³</a>
<a href="register.php" class="btn-secondary">æ°èŠç»é²</a>
</div>
<div class="update-date">
æçµæŽæ°æ¥: 2026幎2æ10æ¥
</div>
</div>
</div>
</body>
</html>
<?php
/**
* èªèšŒãå¿
èŠãªããŒãžçšã®ãã«ããŒé¢æ°
* ãã°ã€ã³ããŠããªãå Žåã¯ãã°ã€ã³ããŒãžã«ãªãã€ã¬ã¯ã
*/
function requireLogin() {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
$currentUrl = $_SERVER['REQUEST_URI'];
header('Location: login.php?redirect=' . urlencode($currentUrl));
exit;
}
}
/**
* ãã°ã€ã³äžã®ãŠãŒã¶ãŒåãååŸ
* @return string|null
*/
function getLoggedInUsername() {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
return $_SESSION['username'] ?? null;
}
/**
* ãã°ã€ã³äžã®ãŠãŒã¶ãŒIDãååŸ
* @return int|null
*/
function getLoggedInUserId() {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
return $_SESSION['user_id'] ?? null;
}
/**
* ãã°ã€ã³ç¶æ
ã確èª
* @return bool
*/
function isLoggedIn() {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
return isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true;
}
<?php
/**
* å
±éããã¿ãŒ
*/
?>
</div> <!-- main-content ã®çµäº -->
<!-- ããã¿ãŒ -->
<footer class="main-footer">
<div class="footer-content">
<p>© <?= date('Y') ?> è£
眮æ
å ±ç®¡çã·ã¹ãã . All rights reserved.</p>
<p class="footer-version">
Built with PHP & MySQL | Version 1.0
</p>
</div>
</footer>
</body>
</html>
<?php
/**
* å
±éããã㌠- ããã²ãŒã·ã§ã³ã¡ãã¥ãŒä»ã
*/
// èªèšŒãã«ããŒé¢æ°ãèªã¿èŸŒã¿
require_once __DIR__ . '/auth_helper.php';
// çŸåšã®ããŒãžãå€å®
$currentPage = basename($_SERVER['PHP_SELF'], '.php');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $pageTitle ?? 'è£
眮æ
å ±ç®¡çã·ã¹ãã ' ?></title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<!-- ã¡ã€ã³ããã²ãŒã·ã§ã³ã㌠-->
<nav class="main-navbar">
<div class="navbar-container">
<!-- ãã©ã³ãã㎠-->
<a href="index.php" class="navbar-brand">
<?php include 'svgs/brand.svg'; ?>
è£
眮æ
å ±ç®¡çã·ã¹ãã
</a>
<div class="navbar-menu">
<!-- ããã²ãŒã·ã§ã³ã¡ãã¥ãŒ -->
<ul class="navbar-nav" id="navbarNav">
<li class="nav-item"> <a href="manage.php" class="nav-link <?= $currentPage === 'manage' ? 'active' : '' ?>" title="è£
眮æ
å ±ç®¡ç">
<div class="nav-icon">
<?php include 'svgs/info.svg'; ?>
</div>
<span class="nav-text nav-text-hidden">è£
眮æ
å ±ç®¡ç</span>
</a>
</li>
<li class="nav-item"> <a href="index.php" class="nav-link <?= $currentPage === 'index' ? 'active' : '' ?>" title="CSVã¢ããããŒã">
<div class="nav-icon">
<?php include 'svgs/upload.svg'; ?>
</div>
<span class="nav-text nav-text-hidden">CSVã¢ããããŒã</span>
</a>
</li>
<li class="nav-item">
<a href="download.php" class="nav-link <?= $currentPage === 'download' ? 'active' : '' ?>" title="CSVããŠã³ããŒã">
<div class="nav-icon">
<?php include 'svgs/download.svg'; ?>
</div>
<span class="nav-text nav-text-hidden">CSVããŠã³ããŒã</span>
</a>
</li>
</ul>
<!-- ãŠãŒã¶ãŒæ
å ± -->
<div class="navbar-user">
<?php if (isLoggedIn()): ?>
<span class="nav-link user-name-display">
<div class="nav-icon">
<span class="user-icon">
<?php include 'svgs/user.svg'; ?>
</span>
</div>
<span class="nav-text"><?= htmlspecialchars(getLoggedInUsername()) ?></span>
</span>
<a href="logout.php" class="nav-link logout-link" title="ãã°ã¢ãŠã">
<div class="nav-icon">
<?php include 'svgs/logout.svg'; ?>
</div>
<span class="nav-text nav-text-hidden">ãã°ã¢ãŠã</span>
</a>
<?php else: ?>
<a href="login.php" class="nav-link login-link" title="ãã°ã€ã³">
<div class="nav-icon">
<?php include 'svgs/login.svg'; ?>
</div>
<span class="nav-text nav-text-hidden">ãã°ã€ã³</span>
</a>
<a href="register.php" class="nav-link register-link" title="æ°èŠç»é²">
<div class="nav-icon">
<?php include 'svgs/register.svg'; ?>
</div>
<span class="nav-text nav-text-hidden">æ°èŠç»é²</span>
</a>
<?php endif; ?>
</div>
</div>
</div>
</nav>
<script>
// ãŠã£ã³ããŠãµã€ãºå€æŽæã«ã¢ãã€ã«ã¡ãã¥ãŒãéãã
window.addEventListener('resize', function() {
if (window.innerWidth > 768) {
const nav = document.getElementById('navbarNav');
nav.classList.remove('show');
}
});
// å€éšã¯ãªãã¯æã«ã¢ãã€ã«ã¡ãã¥ãŒãéãã
document.addEventListener('click', function(event) {
const nav = document.getElementById('navbarNav');
const toggle = document.querySelector('.mobile-menu-toggle');
if (nav && (!nav.contains(event.target) && (!toggle || !toggle.contains(event.target)))) {
nav.classList.remove('show');
}
});
</script>
<?php
/**
* ã¢ããªã±ãŒã·ã§ã³èšå®ãã¡ã€ã«ïŒRenderæ¬çªç°å¢çšïŒ
*/
// ããŒã¿ããŒã¹èšå®ïŒç°å¢å€æ°ããååŸïŒ
// Render PostgreSQLçšèšå®
define('DB_TYPE', getenv('DB_TYPE') ?: 'pgsql'); // 'pgsql' or 'mysql'
define('DB_HOST', getenv('DB_HOST') ?: 'localhost');
define('DB_PORT', getenv('DB_PORT') ?: '5432'); // PostgreSQL: 5432, MySQL: 3306
define('DB_NAME', getenv('DB_NAME') ?: 'device_management');
define('DB_USER', getenv('DB_USER') ?: 'postgres');
define('DB_PASS', getenv('DB_PASS') ?: '');
define('DB_CHARSET', 'utf8'); // PostgreSQL: utf8, MySQL: utf8mb4
// ã¢ããããŒãèšå®
define('UPLOAD_MAX_SIZE', 10 * 1024 * 1024); // 10MB
define('UPLOAD_ALLOWED_TYPES', ['text/csv', 'application/csv', 'text/plain']);
define('UPLOAD_DIR', __DIR__ . '/uploads/');
// ãšã©ãŒã¬ããŒãèšå®ïŒãããã°çš - åé¡è§£æ±ºåŸã¯ç¡å¹åïŒ
ini_set('display_errors', 1);
error_reporting(E_ALL);
// ãšã©ãŒãã°èšå®
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/logs/php_error.log');
// ã¿ã€ã ãŸãŒã³èšå®
date_default_timezone_set('Asia/Tokyo');
// ã»ãã·ã§ã³èšå®
session_start();
// ã¯ã©ã¹ãã¡ã€ã«ã®èªåèªã¿èŸŒã¿
spl_autoload_register(function ($class_name) {
$file = __DIR__ . '/classes/' . $class_name . '.php';
if (file_exists($file)) {
require_once $file;
}
});
/**
* ããŒãã«åã®ãµãã¿ã€ãºé¢æ°
*/
function sanitizeTableName($name) {
// æ¥æ¬èªïŒãã«ããã€ãæåïŒãèš±å¯ããè±æ°åãã¢ã³ããŒã¹ã³ã¢ããã«ããã€ãæå以å€ã_ã«å€æ
return preg_replace('/[^a-zA-Z0-9_\x80-\xFF]/', '_', $name);
}
/**
* HTMLãšã¹ã±ãŒã颿°
* @param string $str
* @return string
*/
function h($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
/**
* CSRFããŒã¯ã³çæ
* @return string
*/
function generateCsrfToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
/**
* CSRFããŒã¯ã³æ€èšŒ
* @param string $token
* @return bool
*/
function validateCsrfToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
/**
* ãã¡ã€ã«åããµãã¿ã€ãº
* @param string $filename
* @return string
*/
function sanitizeFilename($filename) {
// å±éºãªæåãé€å»
$filename = preg_replace('/[^a-zA-Z0-9\-_\.]/', '_', $filename);
// é£ç¶ããããããã¢ã³ããŒã¹ã³ã¢ãåäžã«
$filename = preg_replace('/[_\.]{2,}/', '_', $filename);
return $filename;
}
/**
* CSVããããŒããã«ã©ã å®çŸ©ãçæ
*/
function generateColumnsFromHeader($header) {
$columns = [];
foreach ($header as $columnName) {
$sanitized = preg_replace('/[^a-zA-Z0-9_]/', '_', $columnName);
$columns[] = "`{$sanitized}` TEXT";
}
return implode(', ', $columns);
}
/**
* CSVã«ã©ã åããµãã¿ã€ãº
*/
function sanitizeColumnName($name) {
return preg_replace('/[^a-zA-Z0-9_]/', '_', $name);
}
/**
* å®å
šãªJSONåºå
*/
function jsonResponse($data, $statusCode = 200) {
http_response_code($statusCode);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
/**
* ãšã©ãŒã¬ã¹ãã³ã¹
*/
function errorResponse($message, $statusCode = 400) {
jsonResponse(['success' => false, 'message' => $message], $statusCode);
}
/**
* æåã¬ã¹ãã³ã¹
*/
function successResponse($data, $message = 'Success') {
jsonResponse(['success' => true, 'message' => $message, 'data' => $data]);
}
/**
* ãšã©ãŒã¡ãã»ãŒãžãã»ãã·ã§ã³ã«èšå®
* @param string $message
*/
function setErrorMessage($message) {
$_SESSION['error_message'] = $message;
}
/**
* æåã¡ãã»ãŒãžãã»ãã·ã§ã³ã«èšå®
* @param string $message
*/
function setSuccessMessage($message) {
$_SESSION['success_message'] = $message;
}
/**
* ãšã©ãŒã¡ãã»ãŒãžãååŸããŠåé€
* @return string|null
*/
function getErrorMessage() {
if (isset($_SESSION['error_message'])) {
$message = $_SESSION['error_message'];
unset($_SESSION['error_message']);
return $message;
}
return null;
}
/**
* æåã¡ãã»ãŒãžãååŸããŠåé€
* @return string|null
*/
function getSuccessMessage() {
if (isset($_SESSION['success_message'])) {
$message = $_SESSION['success_message'];
unset($_SESSION['success_message']);
return $message;
}
return null;
}
# MySQL ããã¯ã¢ãã Cronèšå®
# ãã®ãã¡ã€ã«ã®å
容ã crontab ã«è¿œå ããŠãã ãã
# äœ¿ãæ¹: crontab -e ã§ç·šéç»é¢ãéããå¿
èŠãªè¡ãã³ããŒããŠè²Œãä»ããŸã
# ========================================
# ç°å¢å€æ°ã®èšå®
# ========================================
# â»ãããã®å€ã¯å®éã®ç°å¢ã«åãããŠå€æŽããŠãã ãã
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=your-email@example.com
# ããŒã¿ããŒã¹æ¥ç¶æ
å ±
DB_HOST=localhost
DB_PORT=3306
DB_NAME=device_management
DB_USER=root
DB_PASS=
# ãããžã§ã¯ããã£ã¬ã¯ããªã®çµ¶å¯Ÿãã¹ïŒå¿
ã倿ŽããŠãã ããïŒ
PROJECT_DIR=/path/to/1031_Fusion
# ========================================
# ããã¯ã¢ããã¹ã±ãžã¥ãŒã«
# ========================================
# ãæ¥æ¬¡ããã¯ã¢ãããæ¯æ¥åå3æã«å®è¡ïŒ7æ¥åä¿æïŒ
0 3 * * * cd ${PROJECT_DIR} && ./backup_mysql.sh daily >> logs/backup.log 2>&1
# ã鱿¬¡ããã¯ã¢ãããæ¯é±æ¥ææ¥ã®åå4æã«å®è¡ïŒ4é±åä¿æïŒ
0 4 * * 0 cd ${PROJECT_DIR} && ./backup_mysql.sh weekly >> logs/backup.log 2>&1
# ãææ¬¡ããã¯ã¢ãããæ¯æ1æ¥ã®åå5æã«å®è¡ïŒ12ã¶æåä¿æïŒ
0 5 1 * * cd ${PROJECT_DIR} && ./backup_mysql.sh monthly >> logs/backup.log 2>&1
# ========================================
# ãã®ä»ã®ã¹ã±ãžã¥ãŒã«äŸ
# ========================================
# å¶æ¥æéå€ã«ããã¯ã¢ãããåãäŸïŒå¹³æ¥ã®æ·±å€2æïŒ
# 0 2 * * 1-5 cd ${PROJECT_DIR} && ./backup_mysql.sh daily >> logs/backup.log 2>&1
# æ¯æããã¯ã¢ããïŒçæä¿åçšïŒ
# 0 * * * * cd ${PROJECT_DIR} && ./backup_mysql.sh hourly >> logs/backup.log 2>&1
# 6æéããšã«ããã¯ã¢ãã
# 0 */6 * * * cd ${PROJECT_DIR} && ./backup_mysql.sh daily >> logs/backup.log 2>&1
# ========================================
# ããã¯ã¢ãããã°ã®ããŒããŒã·ã§ã³ïŒãªãã·ã§ã³ïŒ
# ========================================
# æ¯é±æææ¥ã®åå6æã«ãã°ãã¢ãŒã«ã€ã
# 0 6 * * 1 cd ${PROJECT_DIR}/logs && gzip -c backup.log > backup_$(date +\%Y\%m\%d).log.gz && > backup.log
# ========================================
# Cronæéæå®ãã©ãŒããã
# ========================================
# å æ æ¥ æ ææ¥
# â â â â â
# â â â â ââ ææ¥ (0-7) â»0ãš7ã¯æ¥ææ¥
# â â â ââââ æ (1-12)
# â â ââââââ æ¥ (1-31)
# â ââââââââ æ (0-23)
# ââââââââââ å (0-59)
#
# ç¹æ®æå:
# * - ãã¹ãŠã®å€
# , - å€ã®ãªã¹ãïŒäŸ: 1,3,5ïŒ
# - - å€ã®ç¯å²ïŒäŸ: 1-5ïŒ
# / - ã¹ãããå€ïŒäŸ: */15 ã¯15åããšïŒ
# ========================================
# èšå®æ¹æ³
# ========================================
# 1. PROJECT_DIR ãå®éã®ãã¹ã«å€æŽ
# 2. ããŒã¿ããŒã¹æ¥ç¶æ
å ±ã確èªã»å€æŽ
# 3. å¿
èŠãªè¡ãã³ããŒ
# 4. crontab -e ãå®è¡
# 5. ã³ããŒããå
容ã貌ãä»ããŠä¿å
#
# ç¢ºèªæ¹æ³:
# crontab -l : çŸåšã®èšå®ã衚瀺
# crontab -r : ãã¹ãŠã®èšå®ãåé€
# crontab -e : èšå®ãç·šé
<?php
/**
* Render ãããã°ããŒãž
* ç°å¢å€æ°ãšããŒã¿ããŒã¹æ¥ç¶ã確èª
*/
// ãšã©ãŒè¡šç€ºãæå¹å
ini_set('display_errors', 1);
error_reporting(E_ALL);
echo "<h1>Render ãããã°æ
å ±</h1>";
// ç°å¢å€æ°ã®ç¢ºèª
echo "<h2>ç°å¢å€æ°</h2>";
echo "<pre>";
echo "DB_HOST: " . (getenv('DB_HOST') ?: $_ENV['DB_HOST'] ?? 'NOT SET') . "\n";
echo "DB_NAME: " . (getenv('DB_NAME') ?: $_ENV['DB_NAME'] ?? 'NOT SET') . "\n";
echo "DB_USER: " . (getenv('DB_USER') ?: $_ENV['DB_USER'] ?? 'NOT SET') . "\n";
echo "DB_PASS: " . ((getenv('DB_PASS') ?: $_ENV['DB_PASS'] ?? null) ? '***SET***' : 'NOT SET') . "\n";
echo "DB_TYPE: " . (getenv('DB_TYPE') ?: $_ENV['DB_TYPE'] ?? 'NOT SET') . "\n";
echo "DB_PORT: " . (getenv('DB_PORT') ?: $_ENV['DB_PORT'] ?? 'NOT SET') . "\n";
echo "</pre>";
// config.phpã®ååšç¢ºèª
echo "<h2>ãã¡ã€ã«ç¢ºèª</h2>";
echo "<pre>";
echo "config.php exists: " . (file_exists('config.php') ? 'YES' : 'NO') . "\n";
echo "config.render.php exists: " . (file_exists('config.render.php') ? 'YES' : 'NO') . "\n";
echo "</pre>";
// PHPæ¡åŒµæ©èœã®ç¢ºèª
echo "<h2>PHPæ¡åŒµæ©èœ</h2>";
echo "<pre>";
echo "PDO: " . (extension_loaded('pdo') ? 'YES' : 'NO') . "\n";
echo "PDO_MySQL: " . (extension_loaded('pdo_mysql') ? 'YES' : 'NO') . "\n";
echo "PDO_PostgreSQL: " . (extension_loaded('pdo_pgsql') ? 'YES' : 'NO') . "\n";
echo "MySQLi: " . (extension_loaded('mysqli') ? 'YES' : 'NO') . "\n";
echo "</pre>";
// ããŒã¿ããŒã¹æ¥ç¶ãã¹ã
echo "<h2>ããŒã¿ããŒã¹æ¥ç¶ãã¹ã</h2>";
echo "<pre>";
if (file_exists('config.php')) {
require_once 'config.php';
try {
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$dbPort = defined('DB_PORT') ? DB_PORT : ($dbType === 'pgsql' ? 5432 : 3306);
echo "æ¥ç¶æ
å ±:\n";
echo " DB_TYPE: {$dbType}\n";
echo " DB_HOST: " . DB_HOST . "\n";
echo " DB_PORT: {$dbPort}\n";
echo " DB_NAME: " . DB_NAME . "\n";
echo " DB_USER: " . DB_USER . "\n\n";
if ($dbType === 'pgsql') {
$dsn = "pgsql:host=" . DB_HOST . ";port={$dbPort};dbname=" . DB_NAME;
} else {
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
}
$pdo = new PDO($dsn, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]);
echo "â
ããŒã¿ããŒã¹æ¥ç¶æå!\n";
// ããŒãã«äžèЧãååŸ
if ($dbType === 'pgsql') {
$stmt = $pdo->query("SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public'");
} else {
$stmt = $pdo->query("SHOW TABLES");
}
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
echo "\nããŒãã«æ°: " . count($tables) . "\n";
if (count($tables) > 0) {
echo "ããŒãã«äžèЧ:\n";
foreach ($tables as $table) {
echo " - $table\n";
}
} else {
echo "â ïž ããŒãã«ãååšããŸãããããŒã¿ããŒã¹åæåãå¿
èŠã§ãã\n";
}
} catch (PDOException $e) {
echo "â ããŒã¿ããŒã¹æ¥ç¶ãšã©ãŒ:\n";
echo $e->getMessage() . "\n";
}
} else {
echo "â config.php ãèŠã€ãããŸãã\n";
}
echo "</pre>";
// ãã£ã¬ã¯ããªã®æš©é確èª
echo "<h2>ãã£ã¬ã¯ããªæš©é</h2>";
echo "<pre>";
$dirs = ['uploads', 'logs', 'classes', 'includes'];
foreach ($dirs as $dir) {
if (file_exists($dir)) {
echo "$dir: " . (is_writable($dir) ? 'â
æžã蟌ã¿å¯' : 'â æžã蟌ã¿äžå¯') . "\n";
} else {
echo "$dir: â ååšããŸãã\n";
}
}
echo "</pre>";
echo "<hr>";
echo "<p><a href='/'>ãããããŒãžã«æ»ã</a></p>";
?>
/* ===================================
è£
眮æ
å ±ç®¡çã·ã¹ãã - ã¡ã€ã³ã¹ã¿ã€ã«ã·ãŒã
=================================== */
/* ãªã»ããCSS */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f5f7fa;
line-height: 1.6;
display: flex;
flex-direction: column;
}
.svg-wrapper {
font-weight: bold;
display: flex;
align-items: center;
}
/* ===================================
ããã²ãŒã·ã§ã³ããŒ
=================================== */
.navbar-menu {
display: flex;
align-items: center;
}
.main-navbar {
background: #004eb1;
box-shadow: 0 2px 20px rgba(0,0,0,0.1);
position: sticky;
top: 0;
z-index: 1000;
}
.navbar-container {
max-width: 1200px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
height: 70px;
}
.navbar-brand {
color: white;
font-size: 24px;
font-weight: bold;
text-decoration: none;
display: flex;
align-items: center;
gap: 10px;
}
.navbar-brand:hover {
color: #e8f2ff;
}
.navbar-brand svg {
width: 32px;
height: 32px;
fill: currentColor;
}
.navbar-nav {
display: flex;
list-style: none;
gap: 5px;
}
.nav-item {
position: relative;
}
.nav-link {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 20px;
color: white;
text-decoration: none;
border-radius: 8px;
transition: background-color 0.3s, justify-content 0.3s;
font-weight: 500;
background: rgba(255,255,255,0.1);
margin: 0 2px;
min-width: 44px;
justify-content: center;
}
.nav-link:hover {
background: rgba(255,255,255,0.2);
justify-content: flex-start;
}
.nav-link.active {
background: rgba(255,255,255,0.25);
}
.nav-icon {
width: 20px;
height: 20px;
}
.nav-icon svg {
width: 100%;
height: 100%;
fill: currentColor;
}
/* ã¡ã€ã³ã³ã³ãã³ããšãªã¢ */
.main-content {
flex: 1;
padding: 40px 20px;
width: 100%;
}
.page-container {
max-width: 1200px;
margin: 0 auto;
}
.page-header {
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 3px solid #e9ecef;
}
.page-header .page-title {
color: #2c3e50;
font-size: 32px;
font-weight: 700;
margin: 0;
display: flex;
align-items: center;
gap: 15px;
}
/* ããã¿ãŒ */
.main-footer {
background-color: #343a40;
color: white;
text-align: center;
padding: 20px 0;
margin-top: auto;
width: 100%;
}
.footer-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.footer-content p {
margin: 0;
}
.footer-version {
font-size: 14px;
color: #adb5bd;
margin-top: 5px;
}
/* ã¢ã©ãŒãã¢ã€ã³ã³ */
.alert {
display: flex;
align-items: flex-start;
gap: 12px;
}
.alert-icon {
width: 20px;
height: 20px;
fill: currentColor;
flex-shrink: 0;
margin-top: 2px;
}
/* èŠåºãå
ã®SVGã¢ã€ã³ã³ */
h2 svg, h3 svg, h4 svg {
fill: currentColor;
}
/* ===================================
å
±éã¹ã¿ã€ã«
=================================== */
.page-title-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.page-title-icon svg {
width: 100%;
height: 100%;
fill: #667eea;
}
.black-svg path{
fill: #333 !important;
}
/* ãªã¹ãã®ããŒã«ãŒãåé€ */
ul, ol {
list-style: none;
padding-left: 0;
margin-left: 0;
}
/* ===================================
ãã©ãŒã å
±éã¹ã¿ã€ã«
=================================== */
.form-section {
background-color: #f8f9fa;
padding: 25px;
border-radius: 8px;
margin-bottom: 30px;
}
.form-section-title {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.form-group {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
}
.flex-col {
flex-direction: column;
align-items: flex-start;
}
.form-group label {
font-weight: bold;
color: #555;
white-space: nowrap;
flex-shrink: 0;
}
.form-control {
width: 100%;
max-width: 300px;
padding: 10px;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 16px;
transition: border-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: #28a745;
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.form-group select,
.form-group input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.form-group select:focus,
.form-group input:focus {
outline: none;
border-color: #007acc;
box-shadow: 0 0 0 2px rgba(0,122,204,0.25);
}
/* ===================================
ãã¿ã³ã¹ã¿ã€ã«
=================================== */
.btn {
padding: 12px 24px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
text-decoration: none;
display: inline-block;
margin-right: 10px;
}
.btn:disabled {
background-color: #6c757d;
cursor: not-allowed;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #5a6268;
}
.btn-success {
background-color: #28a745;
color: white;
}
.btn-success:hover {
background-color: #218838;
}
.btn-export {
padding: 6px 12px;
font-size: 12px;
background-color: #28a745;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-export:hover {
background-color: #218838;
}
.btn-macro {
padding: 6px 12px;
font-size: 12px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
white-space: nowrap;
}
.btn-macro:hover:not(:disabled) {
background-color: #0056b3;
}
.btn-macro:disabled {
background-color: #6c757d;
cursor: not-allowed;
opacity: 0.6;
}
.btn-icon {
fill: white;
width: 12px;
height: 12px;
margin-right: 5px;
}
.search-buttons {
display: flex;
gap: 10px;
justify-content: flex-end;
margin-top: 20px;
}
.search-buttons .btn {
display: flex;
align-items: center;
}
.export-buttons {
display: flex;
gap: 10px;
}
/* ===================================
ã¢ã©ãŒãã»ã¡ãã»ãŒãž
=================================== */
.alert {
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.alert-error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-info {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.info-box {
background-color: #f0f9ff;
border: 1px solid #0284c7;
border-left: 4px solid #0284c7;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.info-box h3 {
color: #0284c7;
margin-top: 0;
display: flex;
align-items: center;
gap: 8px;
}
/* ===================================
çµ±èšæ
å ±ã«ãŒãïŒæ€çŽ¢ããŒãžïŒ
=================================== */
.statistics {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 30px;
}
.stat-card {
color: white;
padding: 20px;
border-radius: 12px;
text-align: center;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.stat-number {
font-size: 2em;
font-weight: bold;
margin-bottom: 5px;
}
.stat-label {
font-size: 0.9em;
opacity: 0.9;
}
/* ===================================
æ€çŽ¢çµæããŒãã«
=================================== */
.search-results {
display: none;
}
.results-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 15px;
background-color: #e9ecef;
border-radius: 4px;
margin-top: 60px;
}
.results-info {
font-weight: bold;
color: #495057;
}
.results-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
font-size: 14px;
}
.results-table th,
.results-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.results-table th {
background-color: #007acc;
color: white;
font-weight: bold;
position: sticky;
top: 0;
}
.results-table tr:hover {
background-color: #f8f9fa;
}
.results-table .text-truncate {
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ===================================
ãã¬ãã¥ãŒããŒãã«ïŒããŠã³ããŒãããŒãžïŒ
=================================== */
.preview-section {
margin-top: 30px;
}
.table-info {
background-color: #e9f7ef;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.preview-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
.preview-table th,
.preview-table td {
padding: 10px;
text-align: left;
border: 1px solid #ddd;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.preview-table th {
background-color: #28a745;
color: white;
font-weight: bold;
position: sticky;
top: 0;
}
.preview-table tr:nth-child(even) {
background-color: #f8f9fa;
}
.preview-table tr:hover {
background-color: #e8f5e8;
}
/* 瞊暪é転æã®ã¹ã¿ã€ã« */
.preview-table.transposed {
width: 100%;
table-layout: auto;
}
.preview-table.transposed th.row-header,
.preview-table.transposed td.row-header {
background-color: #28a745;
color: white;
font-weight: bold;
min-width: 120px;
position: sticky;
left: 39px;
z-index: 11;
border-left: none;
margin-left: -1px;
}
.preview-table.transposed th.checkbox-header,
.preview-table.transposed td.checkbox-cell {
width: 40px;
min-width: 40px;
max-width: 40px;
text-align: center;
background-color: #f8f9fa;
position: sticky;
left: 0;
z-index: 10;
padding: 8px 4px;
border-right: none;
}
.preview-table.transposed td.checkbox-cell input[type="checkbox"] {
margin: 0;
}
/* ===================================
ããŠã³ããŒãã»ã¯ã·ã§ã³
=================================== */
.download-section {
margin: 20px 0;
background-color: #fff3cd;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #ffc107;
}
/* ===================================
ããŒãžããŒã·ã§ã³
=================================== */
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
margin-top: 20px;
}
.pagination button {
padding: 8px 12px;
border: 1px solid #ddd;
background-color: white;
color: #333;
cursor: pointer;
border-radius: 4px;
}
.pagination button:hover:not(:disabled) {
background-color: #007acc;
color: white;
}
.pagination button:disabled {
background-color: #f8f9fa;
color: #6c757d;
cursor: not-allowed;
}
.pagination .current {
background-color: #007acc;
color: white;
font-weight: bold;
}
/* ===================================
ããŒãã£ã³ã°è¡šç€º
=================================== */
.loading {
text-align: center;
padding: 40px;
color: #6c757d;
}
.spinner {
border: 4px solid #f3f3f3;
border-radius: 50%;
border-top: 4px solid #007acc;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* ===================================
空ã®ç¶æ
衚瀺
=================================== */
.empty-state {
text-align: center;
padding: 40px;
color: #6c757d;
}
.empty-state h3 {
margin-bottom: 10px;
}
/* ===================================
ã¢ããããŒãããŒãž - ãã©ãã°&ãããã
=================================== */
input[type="file"] {
width: 100%;
padding: 12px;
border: 2px dashed #ddd;
border-radius: 8px;
background-color: #fafafa;
cursor: pointer;
transition: border-color 0.3s;
}
input[type="file"]:hover {
border-color: #667eea;
}
.drag-drop-area {
width: 100%;
min-height: 150px;
border: 3px dashed #ddd;
border-radius: 12px;
background-color: #fafafa;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 30px;
text-align: center;
position: relative;
}
.drag-drop-area:hover {
border-color: #667eea;
background-color: #f0f4ff;
}
.drag-drop-area.drag-over {
border-color: #667eea;
background-color: #e8f2ff;
}
.drag-drop-area.has-file {
border-color: #059669;
background-color: #f0fdf4;
}
.drag-drop-icon {
width: 48px;
height: 48px;
color: #9ca3af;
margin-bottom: 16px;
}
.drag-drop-area.drag-over .drag-drop-icon {
color: #667eea;
}
.drag-drop-area.has-file .drag-drop-icon {
color: #059669;
}
.drag-drop-text {
color: #6b7280;
font-size: 16px;
font-weight: 500;
margin-bottom: 8px;
}
.drag-drop-subtext {
color: #9ca3af;
font-size: 14px;
}
.file-input-hidden {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
.selected-file-info {
display: none;
margin-top: 15px;
padding: 15px;
background-color: #f0fdf4;
border: 1px solid #bbf7d0;
border-radius: 8px;
}
.selected-file-info.show {
display: block;
}
.file-details {
display: flex;
align-items: center;
gap: 12px;
}
.file-icon {
width: 32px;
height: 32px;
color: #059669;
}
.file-info-text {
flex-grow: 1;
}
.file-name {
font-weight: 600;
color: #065f46;
margin-bottom: 4px;
}
.file-size {
font-size: 14px;
color: #6b7280;
}
.csv-format {
background-color: #f5f5f5;
border: 1px solid #ddd;
border-radius: 3px;
padding: 10px;
font-family: monospace;
font-size: 14px;
overflow-x: auto;
}
.file-info {
font-size: 12px;
color: #666;
margin-top: 5px;
}
.upload-progress {
display: none;
width: 100%;
background-color: #f0f0f0;
border-radius: 5px;
margin-top: 10px;
}
.progress-bar {
width: 0%;
height: 20px;
background-color: #007acc;
border-radius: 5px;
transition: width 0.3s;
}
/* ===================================
ãŠãŒã¶ãŒèªèšŒé¢é£ã®ã¹ã¿ã€ã«
=================================== */
.navbar-user {
display: flex;
align-items: center;
gap: 5px;
padding-left: 20px;
margin-left: 20px;
border-left: 2px solid rgba(255, 255, 255, 0.5);
}
/* ãŠãŒã¶ãŒã¢ã€ã³ã³ */
.user-icon {
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
/* ãŠãŒã¶ãŒå衚瀺ã¯åžžã«å·Šæã */
.user-name-display {
justify-content: flex-start !important;
}
/* ãããŒåã«é衚瀺ã«ããããã¹ã */
.nav-text-hidden {
display: inline-block;
max-width: 0;
opacity: 0;
overflow: hidden;
white-space: nowrap;
transition: max-width 0.3s ease, opacity 0.3s ease;
vertical-align: middle;
}
/* ãããŒæã«ããã¹ãã衚瀺 */
.nav-link:hover .nav-text-hidden {
max-width: 150px;
opacity: 1;
}
/* èªèšŒãªã³ã¯ã®è²èšå® */
.logout-link,
.login-link {
background: rgba(255, 255, 255, 0.1);
}
.logout-link:hover,
.login-link:hover {
background: rgba(255, 255, 255, 0.2);
}
.register-link {
background: #4CAF50;
}
.register-link:hover {
background: #45a049;
}
/* ã¬ã¹ãã³ã·ãå¯Ÿå¿ */
@media (max-width: 768px) {
.navbar-user {
gap: 5px;
padding-left: 10px;
margin-left: 10px;
}
.user-name-display .nav-text {
display: none;
}
.nav-text-hidden {
display: none;
}
.nav-link:hover .nav-text-hidden {
display: none;
}
}
<?php
/**
* ã¢ããªã±ãŒã·ã§ã³èšå®ãã¡ã€ã«ïŒãµã³ãã«ïŒ
*
* ãã®ãã¡ã€ã«ãã³ããŒã㊠config.php ãäœæãã
* å®éã®ç°å¢ã«åãããŠèšå®ã倿ŽããŠãã ããã
*/
// ããŒã¿ããŒã¹èšå®ïŒç°å¢å€æ°å¯Ÿå¿ïŒ
define('DB_HOST', $_ENV['DB_HOST'] ?? 'localhost');
define('DB_NAME', $_ENV['DB_NAME'] ?? 'device_management');
define('DB_USER', $_ENV['DB_USER'] ?? 'root');
define('DB_PASS', $_ENV['DB_PASS'] ?? '');
define('DB_CHARSET', 'utf8mb4');
// ã¢ããããŒãèšå®
define('UPLOAD_MAX_SIZE', 10 * 1024 * 1024); // 10MB
define('UPLOAD_ALLOWED_TYPES', ['text/csv', 'application/csv', 'text/plain']);
define('UPLOAD_DIR', __DIR__ . '/uploads/');
// ãšã©ãŒã¬ããŒãèšå®ïŒæ¬çªç°å¢ã§ã¯ç¡å¹ã«ããïŒ
ini_set('display_errors', 1);
error_reporting(E_ALL);
// ãšã©ãŒãã°èšå®
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/logs/php_error.log');
// ã¿ã€ã ãŸãŒã³èšå®
date_default_timezone_set('Asia/Tokyo');
// ã»ãã·ã§ã³èšå®
session_start();
// ã¯ã©ã¹ãã¡ã€ã«ã®èªåèªã¿èŸŒã¿
spl_autoload_register(function ($class_name) {
$file = __DIR__ . '/classes/' . $class_name . '.php';
if (file_exists($file)) {
require_once $file;
}
});
/**
* ããŒãã«åã®ãµãã¿ã€ãºé¢æ°
*/
function sanitizeTableName($name) {
return preg_replace('/[^a-zA-Z0-9_]/', '_', $name);
}
/**
* HTMLåºåçšã®ãšã¹ã±ãŒã颿°
*/
function h($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
/**
* ãšã©ãŒã¡ãã»ãŒãžãã»ãã·ã§ã³ã«ä¿å
*/
function setErrorMessage($message) {
$_SESSION['error_message'] = $message;
}
/**
* ãšã©ãŒã¡ãã»ãŒãžãååŸããŠåé€
*/
function getErrorMessage() {
if (isset($_SESSION['error_message'])) {
$message = $_SESSION['error_message'];
unset($_SESSION['error_message']);
return $message;
}
return null;
}
/**
* æåã¡ãã»ãŒãžãã»ãã·ã§ã³ã«ä¿å
*/
function setSuccessMessage($message) {
$_SESSION['success_message'] = $message;
}
/**
* æåã¡ãã»ãŒãžãååŸããŠåé€
*/
function getSuccessMessage() {
if (isset($_SESSION['success_message'])) {
$message = $_SESSION['success_message'];
unset($_SESSION['success_message']);
return $message;
}
return null;
}
/**
* CSVããããŒã®ãµãã¿ã€ãºïŒç¹æ®æåãåé€ïŒ
*/
function sanitizeColumnName($name) {
// BOMãç¹æ®æåã空çœãåé€ããŠã¢ã³ããŒã¹ã³ã¢ã«å€æ
$name = str_replace("\xEF\xBB\xBF", '', $name); // BOMåé€
$name = trim($name);
$name = preg_replace('/[^\x20-\x7E]/', '', $name); // å¶åŸ¡æåãåé€
$name = preg_replace('/[^a-zA-Z0-9_\x80-\xFF]/', '_', $name); // è±æ°åãã¢ã³ããŒã¹ã³ã¢ããã«ããã€ãæå以å€ã_ã«
return $name;
}
/**
* æ¡åŒµã«ã©ã ã®ããªããŒã·ã§ã³
*/
function validateExtendedColumn($columnName) {
// äºçŽèªãã§ãã¯
$reservedWords = [
'SELECT', 'INSERT', 'UPDATE', 'DELETE', 'DROP', 'CREATE', 'ALTER',
'TABLE', 'DATABASE', 'INDEX', 'PRIMARY', 'KEY', 'FOREIGN', 'REFERENCES',
'WHERE', 'FROM', 'JOIN', 'UNION', 'GROUP', 'ORDER', 'HAVING', 'LIMIT'
];
if (in_array(strtoupper($columnName), $reservedWords)) {
return false;
}
// æ¢åã®åºå®ã«ã©ã åãã§ãã¯
$fixedColumns = [
'primary_key', 'service_name', 'device_type', 'device_name',
'device_ip', 'username', 'password', 'created_at', 'updated_at'
];
if (in_array($columnName, $fixedColumns)) {
return false;
}
// é·ããã§ãã¯ïŒæå€§64æåïŒ
if (strlen($columnName) > 64) {
return false;
}
// å
é ãæ°åã§ãªãããšã確èª
if (preg_match('/^\d/', $columnName)) {
return false;
}
return true;
}
<?php
/**
* Dockerç°å¢çšã¢ããªã±ãŒã·ã§ã³èšå®ãã¡ã€ã«
*/
// ããŒã¿ããŒã¹èšå®ïŒDockerç°å¢çšïŒ
define('DB_HOST', $_ENV['DB_HOST'] ?? 'mysql');
define('DB_NAME', $_ENV['DB_NAME'] ?? 'device_management');
define('DB_USER', $_ENV['DB_USER'] ?? 'root');
define('DB_PASS', $_ENV['DB_PASS'] ?? 'rootpassword');
define('DB_CHARSET', 'utf8mb4');
// ã¢ããããŒãèšå®
define('UPLOAD_MAX_SIZE', 10 * 1024 * 1024); // 10MB
define('UPLOAD_ALLOWED_TYPES', ['text/csv', 'application/csv', 'text/plain']);
define('UPLOAD_DIR', __DIR__ . '/uploads/');
// ãšã©ãŒã¬ããŒãèšå®ïŒDockerç°å¢ã§ã¯æå¹ïŒ
ini_set('display_errors', 1);
error_reporting(E_ALL);
// ã¿ã€ã ãŸãŒã³èšå®
date_default_timezone_set('Asia/Tokyo');
// ã»ãã·ã§ã³èšå®
session_start();
// ãªãŒãããŒãèšå®
function autoload($className) {
$classFile = __DIR__ . '/classes/' . $className . '.php';
if (file_exists($classFile)) {
require_once $classFile;
}
}
spl_autoload_register('autoload');
// å
±é颿°
/**
* HTMLãšã¹ã±ãŒã
* @param string $str
* @return string
*/
function h($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
/**
* CSRFããŒã¯ã³çæ
* @return string
*/
function generateCsrfToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
/**
* CSRFããŒã¯ã³æ€èšŒ
* @param string $token
* @return bool
*/
function validateCsrfToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
/**
* ãã¡ã€ã«åããµãã¿ã€ãº
* @param string $filename
* @return string
*/
function sanitizeFilename($filename) {
// å±éºãªæåãé€å»
$filename = preg_replace('/[^a-zA-Z0-9\-_\.]/', '_', $filename);
// é£ç¶ããããããã¢ã³ããŒã¹ã³ã¢ãåäžã«
$filename = preg_replace('/[_\.]{2,}/', '_', $filename);
return $filename;
}
/**
* ããŒã¿ããŒã¹ã®ããŒãã«åãšããŠäœ¿çšå¯èœãªæååã«å€æ
* @param string $str
* @return string
*/
function sanitizeTableName($str) {
// æ¥æ¬èªæåã¯æ®ããç¹æ®æåã®ã¿é€å»
$str = preg_replace('/[^\p{L}\p{N}_]/u', '_', $str);
// é£ç¶ããã¢ã³ããŒã¹ã³ã¢ãåäžã«
$str = preg_replace('/_+/', '_', $str);
// å
é æ«å°Ÿã®ã¢ã³ããŒã¹ã³ã¢ãé€å»
$str = trim($str, '_');
return $str;
}
/**
* ãšã©ãŒã¡ãã»ãŒãžãã»ãã·ã§ã³ã«èšå®
* @param string $message
*/
function setErrorMessage($message) {
$_SESSION['error_message'] = $message;
}
/**
* æåã¡ãã»ãŒãžãã»ãã·ã§ã³ã«èšå®
* @param string $message
*/
function setSuccessMessage($message) {
$_SESSION['success_message'] = $message;
}
/**
* ãšã©ãŒã¡ãã»ãŒãžãååŸããŠåé€
* @return string|null
*/
function getErrorMessage() {
if (isset($_SESSION['error_message'])) {
$message = $_SESSION['error_message'];
unset($_SESSION['error_message']);
return $message;
}
return null;
}
/**
* æåã¡ãã»ãŒãžãååŸããŠåé€
* @return string|null
*/
function getSuccessMessage() {
if (isset($_SESSION['success_message'])) {
$message = $_SESSION['success_message'];
unset($_SESSION['success_message']);
return $message;
}
return null;
}
/**
* Dockerç°å¢ã®å¥å
šæ§ãã§ãã¯
* @return array
*/
function checkDockerEnvironment() {
$checks = [
'database_connection' => false,
'upload_directory' => false,
'required_extensions' => false
];
// ããŒã¿ããŒã¹æ¥ç¶ãã§ãã¯
try {
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_CHARSET, 'mysql', 3306);
$database->connect();
$checks['database_connection'] = true;
} catch (Exception $e) {
// æ¥ç¶å€±æ
}
// ã¢ããããŒããã£ã¬ã¯ããªãã§ãã¯
if (is_dir(UPLOAD_DIR) && is_writable(UPLOAD_DIR)) {
$checks['upload_directory'] = true;
}
// å¿
èŠãªæ¡åŒµæ©èœãã§ãã¯
if (extension_loaded('pdo') && extension_loaded('pdo_mysql')) {
$checks['required_extensions'] = true;
}
return $checks;
}
?>
<?php
/**
* ãŠãŒã¶ãŒç®¡çã¯ã©ã¹
* ãã¹ã¯ãŒãã¯bcryptããã·ã¥ã§å®å
šã«ä¿ç®¡
*/
class User {
private $db;
private $dbType;
public function __construct(Database $database) {
$this->db = $database;
$this->dbType = $database->getDbType();
}
/**
* ãŠãŒã¶ãŒãç»é²
* @param string $username ãŠãŒã¶ãŒå
* @param string $password ãã¹ã¯ãŒãïŒå¹³æïŒ
* @return array æåæ: ['success' => true, 'user_id' => id], 倱ææ: ['success' => false, 'error' => message]
*/
public function register($username, $password) {
try {
// å
¥åæ€èšŒ
if (empty($username) || empty($password)) {
return ['success' => false, 'error' => 'ãŠãŒã¶ãŒåãšãã¹ã¯ãŒãã¯å¿
é ã§ã'];
}
if (strlen($username) < 3) {
return ['success' => false, 'error' => 'ãŠãŒã¶ãŒåã¯3æå以äžã§å
¥åããŠãã ãã'];
}
if (strlen($password) < 6) {
return ['success' => false, 'error' => 'ãã¹ã¯ãŒãã¯6æå以äžã§å
¥åããŠãã ãã'];
}
// ãŠãŒã¶ãŒåã®éè€ãã§ãã¯
if ($this->usernameExists($username)) {
return ['success' => false, 'error' => 'ãã®ãŠãŒã¶ãŒåã¯æ¢ã«äœ¿çšãããŠããŸã'];
}
// ãã¹ã¯ãŒããbcryptã§ããã·ã¥åïŒã³ã¹ã10ïŒ
$passwordHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 10]);
if ($passwordHash === false) {
return ['success' => false, 'error' => 'ãã¹ã¯ãŒãã®ããã·ã¥åã«å€±æããŸãã'];
}
// ããŒã¿ããŒã¹ã«ç»é²
$conn = $this->db->connect();
$sql = "INSERT INTO users (username, password_hash) VALUES (:username, :password_hash)";
$stmt = $conn->prepare($sql);
$stmt->execute([
':username' => $username,
':password_hash' => $passwordHash
]);
$userId = $conn->lastInsertId();
return [
'success' => true,
'user_id' => $userId,
'message' => 'ãŠãŒã¶ãŒç»é²ãå®äºããŸãã'
];
} catch (PDOException $e) {
error_log("User registration error: " . $e->getMessage());
return ['success' => false, 'error' => 'ãŠãŒã¶ãŒç»é²ã«å€±æããŸãã'];
}
}
/**
* ãŠãŒã¶ãŒåã®ååšç¢ºèª
* @param string $username
* @return bool
*/
private function usernameExists($username) {
try {
$conn = $this->db->connect();
$sql = "SELECT COUNT(*) FROM users WHERE username = :username";
$stmt = $conn->prepare($sql);
$stmt->execute([':username' => $username]);
return $stmt->fetchColumn() > 0;
} catch (PDOException $e) {
error_log("Username check error: " . $e->getMessage());
return false;
}
}
/**
* ãã°ã€ã³èªèšŒ
* @param string $username ãŠãŒã¶ãŒå
* @param string $password ãã¹ã¯ãŒãïŒå¹³æïŒ
* @return array æåæ: ['success' => true, 'user' => [user data]], 倱ææ: ['success' => false, 'error' => message]
*/
public function login($username, $password) {
try {
// å
¥åæ€èšŒ
if (empty($username) || empty($password)) {
return ['success' => false, 'error' => 'ãŠãŒã¶ãŒåãšãã¹ã¯ãŒããå
¥åããŠãã ãã'];
}
// ãŠãŒã¶ãŒæ
å ±ãååŸ
$conn = $this->db->connect();
$sql = "SELECT id, username, password_hash, is_active FROM users WHERE username = :username";
$stmt = $conn->prepare($sql);
$stmt->execute([':username' => $username]);
$user = $stmt->fetch();
if (!$user) {
return ['success' => false, 'error' => 'ãŠãŒã¶ãŒåãŸãã¯ãã¹ã¯ãŒããæ£ãããããŸãã'];
}
// ã¢ã«ãŠã³ãã®æå¹æ§ãã§ãã¯
if (!$user['is_active']) {
return ['success' => false, 'error' => 'ãã®ã¢ã«ãŠã³ãã¯ç¡å¹ã§ã'];
}
// ãã¹ã¯ãŒãæ€èšŒ
if (!password_verify($password, $user['password_hash'])) {
return ['success' => false, 'error' => 'ãŠãŒã¶ãŒåãŸãã¯ãã¹ã¯ãŒããæ£ãããããŸãã'];
}
// ãã¹ã¯ãŒãã®åããã·ã¥ãå¿
èŠããã§ãã¯ïŒbcryptã®ã³ã¹ãã倿Žãããå ŽåïŒ
if (password_needs_rehash($user['password_hash'], PASSWORD_BCRYPT, ['cost' => 10])) {
$newHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 10]);
$updateSql = "UPDATE users SET password_hash = :password_hash WHERE id = :id";
$updateStmt = $conn->prepare($updateSql);
$updateStmt->execute([
':password_hash' => $newHash,
':id' => $user['id']
]);
}
// æçµãã°ã€ã³æ¥æãæŽæ°
$updateLoginSql = "UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = :id";
$updateLoginStmt = $conn->prepare($updateLoginSql);
$updateLoginStmt->execute([':id' => $user['id']]);
// ã»ãã·ã§ã³ã«ä¿å
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['logged_in'] = true;
// ã»ãã·ã§ã³ãã€ãžã£ãã¯å¯Ÿç
session_regenerate_id(true);
return [
'success' => true,
'user' => [
'id' => $user['id'],
'username' => $user['username']
],
'message' => 'ãã°ã€ã³ããŸãã'
];
} catch (PDOException $e) {
error_log("Login error: " . $e->getMessage());
return ['success' => false, 'error' => 'ãã°ã€ã³ã«å€±æããŸãã'];
}
}
/**
* ãã°ã¢ãŠã
*/
public function logout() {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$_SESSION = [];
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
}
/**
* ãã°ã€ã³ç¶æ
ã®ç¢ºèª
* @return bool
*/
public function isLoggedIn() {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
return isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true;
}
/**
* çŸåšã®ãŠãŒã¶ãŒæ
å ±ãååŸ
* @return array|null
*/
public function getCurrentUser() {
if (!$this->isLoggedIn()) {
return null;
}
return [
'id' => $_SESSION['user_id'] ?? null,
'username' => $_SESSION['username'] ?? null
];
}
/**
* ãŠãŒã¶ãŒIDãããŠãŒã¶ãŒæ
å ±ãååŸ
* @param int $userId
* @return array|null
*/
public function getUserById($userId) {
try {
$conn = $this->db->connect();
$sql = "SELECT id, username, created_at, last_login, is_active FROM users WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->execute([':id' => $userId]);
return $stmt->fetch();
} catch (PDOException $e) {
error_log("Get user error: " . $e->getMessage());
return null;
}
}
/**
* ãã¹ã¯ãŒã倿Ž
* @param int $userId
* @param string $currentPassword çŸåšã®ãã¹ã¯ãŒã
* @param string $newPassword æ°ãããã¹ã¯ãŒã
* @return array
*/
public function changePassword($userId, $currentPassword, $newPassword) {
try {
// çŸåšã®ãã¹ã¯ãŒããæ€èšŒ
$conn = $this->db->connect();
$sql = "SELECT password_hash FROM users WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->execute([':id' => $userId]);
$user = $stmt->fetch();
if (!$user) {
return ['success' => false, 'error' => 'ãŠãŒã¶ãŒãèŠã€ãããŸãã'];
}
if (!password_verify($currentPassword, $user['password_hash'])) {
return ['success' => false, 'error' => 'çŸåšã®ãã¹ã¯ãŒããæ£ãããããŸãã'];
}
// æ°ãããã¹ã¯ãŒãã®æ€èšŒ
if (strlen($newPassword) < 6) {
return ['success' => false, 'error' => 'æ°ãããã¹ã¯ãŒãã¯6æå以äžã§å
¥åããŠãã ãã'];
}
// æ°ãããã¹ã¯ãŒããããã·ã¥å
$newHash = password_hash($newPassword, PASSWORD_BCRYPT, ['cost' => 10]);
// ãã¹ã¯ãŒããæŽæ°
$updateSql = "UPDATE users SET password_hash = :password_hash WHERE id = :id";
$updateStmt = $conn->prepare($updateSql);
$updateStmt->execute([
':password_hash' => $newHash,
':id' => $userId
]);
return ['success' => true, 'message' => 'ãã¹ã¯ãŒãã倿ŽããŸãã'];
} catch (PDOException $e) {
error_log("Password change error: " . $e->getMessage());
return ['success' => false, 'error' => 'ãã¹ã¯ãŒã倿Žã«å€±æããŸãã'];
}
}
}
<?php
/**
* Teraterm ãã¯ã (.ttl) ãã¡ã€ã«çæã¯ã©ã¹
*
* SSHæ¥ç¶çšã®Teratermãã¯ããã¡ã€ã«ãçæããŸã
*/
class TeratermMacroGenerator
{
private string $hostAddr;
private string $username;
private string $password;
private int $port;
/**
* ã³ã³ã¹ãã©ã¯ã¿
*
* @param string $hostAddr ãã¹ãã¢ãã¬ã¹ (IPã¢ãã¬ã¹ãŸãã¯ãã¹ãå)
* @param string $username ãŠãŒã¶ãŒå
* @param string $password ãã¹ã¯ãŒã
* @param int $port SSHããŒãçªå· (ããã©ã«ã: 22)
*/
public function __construct(
string $hostAddr,
string $username,
string $password,
int $port = 22
) {
$this->hostAddr = $hostAddr;
$this->username = $username;
$this->password = $password;
$this->port = $port;
}
/**
* Teratermãã¯ãã®å
容ãçæ
*
* @return string ãã¯ããã¡ã€ã«ã®å
容
*/
public function generate(): string
{
$macro = "; --- Configuration ---\n";
$macro .= "HOSTADDR = '{$this->hostAddr}'\n";
$macro .= "USERNAME = '{$this->username}'\n";
$macro .= "PASSWORD = '{$this->password}'\n";
$macro .= "\n";
$macro .= "; --- Setup Connection String ---\n";
$macro .= "COMMAND = HOSTADDR\n";
$macro .= "strconcat COMMAND ':{$this->port} /ssh /2 /auth=password /user='\n";
$macro .= "strconcat COMMAND USERNAME\n";
$macro .= "strconcat COMMAND ' /passwd='\n";
$macro .= "strconcat COMMAND PASSWORD\n";
$macro .= "\n";
$macro .= "; --- Execution ---\n";
$macro .= "connect COMMAND\n";
return $macro;
}
/**
* ãã¯ããã¡ã€ã«ãæå®ãã¹ã«ä¿å
*
* @param string $filePath ä¿åå
ãã¡ã€ã«ãã¹
* @return bool æåããå Žåtrue
*/
public function saveToFile(string $filePath): bool
{
$content = $this->generate();
return file_put_contents($filePath, $content) !== false;
}
/**
* ãã¯ããã¡ã€ã«ãããŠã³ããŒãçšã«åºå
*
* @param string $filename ããŠã³ããŒããã¡ã€ã«å (ããã©ã«ã: connection.ttl)
*/
public function download(string $filename = 'connection.ttl'): void
{
$content = $this->generate();
header('Content-Type: text/plain; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . strlen($content));
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
echo $content;
exit;
}
/**
* è€æ°ã®æ¥ç¶æ
å ±ããäžæ¬ã§ãã¯ããçæ
*
* @param array $connections æ¥ç¶æ
å ±ã®é
å
* [['host' => '...', 'user' => '...', 'pass' => '...', 'port' => 22], ...]
* @param string $outputDir åºåãã£ã¬ã¯ããª
* @return array çæããããã¡ã€ã«ãã¹ã®é
å
*/
public static function generateBatch(array $connections, string $outputDir): array
{
$generatedFiles = [];
if (!is_dir($outputDir)) {
mkdir($outputDir, 0755, true);
}
foreach ($connections as $index => $conn) {
$hostAddr = $conn['host'] ?? '';
$username = $conn['user'] ?? '';
$password = $conn['pass'] ?? '';
$port = $conn['port'] ?? 22;
if (empty($hostAddr) || empty($username)) {
continue;
}
$generator = new self($hostAddr, $username, $password, $port);
// ãã¡ã€ã«åãçæ (ãã¹ãå_ãŠãŒã¶ãŒå.ttl)
$safeName = preg_replace('/[^a-zA-Z0-9._-]/', '_', $hostAddr);
$filename = "{$safeName}_{$username}.ttl";
$filePath = rtrim($outputDir, '/\\') . DIRECTORY_SEPARATOR . $filename;
if ($generator->saveToFile($filePath)) {
$generatedFiles[] = $filePath;
}
}
return $generatedFiles;
}
/**
* CSVãã¡ã€ã«ããæ¥ç¶æ
å ±ãèªã¿èŸŒãã§ãã¯ããçæ
*
* @param string $csvPath CSVãã¡ã€ã«ãã¹
* @param string $outputDir åºåãã£ã¬ã¯ããª
* @param array $columnMapping ã«ã©ã ãããã³ã° ['host' => 'IP', 'user' => 'User', 'pass' => 'Password']
* @return array çæããããã¡ã€ã«ãã¹ã®é
å
*/
public static function generateFromCsv(
string $csvPath,
string $outputDir,
array $columnMapping = ['host' => 'IP', 'user' => 'User', 'pass' => 'Password', 'port' => 'Port']
): array {
if (!file_exists($csvPath)) {
throw new Exception("CSVãã¡ã€ã«ãèŠã€ãããŸãã: {$csvPath}");
}
$connections = [];
$handle = fopen($csvPath, 'r');
if ($handle === false) {
throw new Exception("CSVãã¡ã€ã«ãéããŸãã: {$csvPath}");
}
// ããããŒè¡ãååŸ
$headers = fgetcsv($handle);
if ($headers === false) {
fclose($handle);
throw new Exception("CSVãã¡ã€ã«ã空ã§ã");
}
// ã«ã©ã ã€ã³ããã¯ã¹ãååŸ
$hostIndex = array_search($columnMapping['host'], $headers);
$userIndex = array_search($columnMapping['user'], $headers);
$passIndex = array_search($columnMapping['pass'], $headers);
$portIndex = isset($columnMapping['port']) ? array_search($columnMapping['port'], $headers) : false;
// ããŒã¿è¡ãèªã¿èŸŒã¿
while (($row = fgetcsv($handle)) !== false) {
$conn = [
'host' => $hostIndex !== false ? ($row[$hostIndex] ?? '') : '',
'user' => $userIndex !== false ? ($row[$userIndex] ?? '') : '',
'pass' => $passIndex !== false ? ($row[$passIndex] ?? '') : '',
'port' => $portIndex !== false && isset($row[$portIndex]) ? (int)$row[$portIndex] : 22
];
if (!empty($conn['host']) && !empty($conn['user'])) {
$connections[] = $conn;
}
}
fclose($handle);
return self::generateBatch($connections, $outputDir);
}
// Getter ã¡ãœãã
public function getHostAddr(): string
{
return $this->hostAddr;
}
public function getUsername(): string
{
return $this->username;
}
public function getPort(): int
{
return $this->port;
}
}
<?php
/**
* è£
眮æ
å ±ç®¡çã¯ã©ã¹
*/
class DeviceManager {
private $database;
public function __construct(Database $database) {
$this->database = $database;
}
/**
* è£
眮æ
å ±ããŒãã«ãååšããããã§ãã¯
* @return bool
*/
public function deviceInfoTableExists() {
return $this->database->tableExists('device_info');
}
/**
* è£
眮æ
å ±ããŒãã«ãäœæ
* @return bool
* @throws Exception
*/
public function createDeviceInfoTable() {
$isPgsql = $this->database->getDbType() === 'pgsql';
if ($isPgsql) {
// PostgreSQLçšSQL
$sql = "
CREATE TABLE IF NOT EXISTS device_info (
primary_key VARCHAR(500) NOT NULL PRIMARY KEY,
service_name VARCHAR(100) NOT NULL,
device_type VARCHAR(100) NOT NULL,
device_name VARCHAR(100) NOT NULL,
login_ip VARCHAR(45),
username1 VARCHAR(100) NOT NULL,
password1 VARCHAR(255),
username2 VARCHAR(100),
password2 VARCHAR(255),
username3 VARCHAR(100),
password3 VARCHAR(255),
username4 VARCHAR(100),
password4 VARCHAR(255),
username5 VARCHAR(100),
password5 VARCHAR(255),
username6 VARCHAR(100),
password6 VARCHAR(255),
username7 VARCHAR(100),
password7 VARCHAR(255),
username8 VARCHAR(100),
password8 VARCHAR(255),
username9 VARCHAR(100),
password9 VARCHAR(255),
username10 VARCHAR(100),
password10 VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
";
$this->database->execute($sql);
// ã€ã³ããã¯ã¹äœæ
$this->database->execute("CREATE INDEX IF NOT EXISTS idx_service_device_type ON device_info (service_name, device_type)");
$this->database->execute("CREATE INDEX IF NOT EXISTS idx_device_info ON device_info (service_name, device_type, device_name, username1)");
} else {
// MySQLçšSQL
$sql = "
CREATE TABLE IF NOT EXISTS device_info (
primary_key VARCHAR(500) NOT NULL PRIMARY KEY COMMENT 'ãµãŒãã¹å_è£
眮皮å¥å_è£
眮å_ãŠãŒã¶åã®è€åããŒ',
service_name VARCHAR(100) NOT NULL COMMENT 'ãµãŒãã¹å',
device_type VARCHAR(100) NOT NULL COMMENT 'è£
眮皮å¥',
device_name VARCHAR(100) NOT NULL COMMENT 'è£
眮åç§°',
login_ip VARCHAR(45) COMMENT 'ãã°ã€ã³IP',
username1 VARCHAR(100) NOT NULL COMMENT 'ãŠãŒã¶ãŒå1',
password1 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã1',
username2 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå2',
password2 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã2',
username3 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå3',
password3 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã3',
username4 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå4',
password4 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã4',
username5 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå5',
password5 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã5',
username6 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå6',
password6 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã6',
username7 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå7',
password7 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã7',
username8 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå8',
password8 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã8',
username9 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå9',
password9 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã9',
username10 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå10',
password10 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã10',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'äœææ¥æ',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æŽæ°æ¥æ',
INDEX idx_service_device_type (service_name, device_type),
INDEX idx_device_info (service_name, device_type, device_name, username1)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='è£
çœ®åºæ¬æ
å ±ããŒãã«'
";
$this->database->execute($sql);
}
return true;
}
/**
* åçããŒãã«ãååšããããã§ãã¯
* @param string $tableName
* @return bool
*/
public function dynamicTableExists($tableName) {
return $this->database->tableExists($tableName);
}
/**
* åçããŒãã«ãäœæ
* @param string $tableName
* @param string $primaryKeyColumn
* @param array $extendedColumns
* @return bool
* @throws Exception
*/
public function createDynamicTable($tableName, $primaryKeyColumn, $extendedColumns) {
// ããŒãã«åã®ã¿ãµãã¿ã€ãºïŒã«ã©ã åã¯æ¥æ¬èªãä¿æïŒ
$tableName = sanitizeTableName($tableName);
$isPgsql = $this->database->getDbType() === 'pgsql';
$columnDefinitions = [];
// MySQL/PostgreSQLã§é©åãªã¯ã©ãŒãæåã䜿çš
$quote = $isPgsql ? '"' : '`';
$columnDefinitions[] = "{$quote}{$primaryKeyColumn}{$quote} VARCHAR(500) NOT NULL PRIMARY KEY";
foreach ($extendedColumns as $column) {
// ã«ã©ã åã¯æ¥æ¬èªã®ãŸãŸäœ¿çšãé©åãªã¯ã©ãŒãã§ãšã¹ã±ãŒã
$columnDefinitions[] = "{$quote}{$column}{$quote} TEXT";
}
$columnDefinitions[] = "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP";
if ($isPgsql) {
$columnDefinitions[] = "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP";
} else {
$columnDefinitions[] = "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æŽæ°æ¥æ'";
}
if ($isPgsql) {
$sql = "
CREATE TABLE IF NOT EXISTS \"{$tableName}\" (
" . implode(",\n ", $columnDefinitions) . "
)
";
} else {
$sql = "
CREATE TABLE IF NOT EXISTS `{$tableName}` (
" . implode(",\n ", $columnDefinitions) . "
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='åçããŒãã«: {$tableName}'
";
}
try {
error_log("Creating dynamic table SQL: " . $sql);
$this->database->execute($sql);
return true;
} catch (Exception $e) {
error_log("Dynamic table creation error: " . $e->getMessage());
error_log("SQL: " . $sql);
throw new Exception("åçããŒãã« '{$tableName}' ã®äœæã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* è£
眮æ
å ±ãæ¿å
¥ãŸãã¯æŽæ°ïŒåçã«ã©ã 察å¿ïŒ
* @param array $deviceData
* @param array $additionalData 远å ã«ã©ã ããŒã¿
* @return bool
* @throws Exception
*/
public function insertOrUpdateDeviceInfo($deviceData, $additionalData = []) {
$isPgsql = $this->database->getDbType() === 'pgsql';
// åºæ¬ã«ã©ã
$baseColumns = ['primary_key', 'service_name', 'device_type', 'device_name', 'login_ip', 'username1', 'password1'];
for ($i = 2; $i <= 10; $i++) {
$baseColumns[] = "username{$i}";
$baseColumns[] = "password{$i}";
}
$columns = $baseColumns;
$placeholders = array_map(function($col) { return ":{$col}"; }, $columns);
$updateColumns = array_diff($columns, ['primary_key']);
$params = $deviceData;
// äœæè
ã»æŽæ°è
ã®æ
å ±ã远å
require_once __DIR__ . '/../includes/auth_helper.php';
$currentUser = getLoggedInUsername();
// created_byã¯æ°èŠäœææã®ã¿èšå®ïŒON DUPLICATE KEY UPDATEã§ã¯æŽæ°ãããªãïŒ
if ($currentUser) {
$columns[] = 'created_by';
$placeholders[] = ':created_by';
$params['created_by'] = $currentUser;
// updated_byã¯åžžã«èšå®
$columns[] = 'updated_by';
$placeholders[] = ':updated_by';
$updateColumns[] = 'updated_by';
$params['updated_by'] = $currentUser;
}
// device_infoããŒãã«ã®æ¢åã«ã©ã ãååŸ
$existingColumns = $this->getTableColumns('device_info');
// 远å ããŒã¿ãããå Žåãdevice_infoã«ååšããã«ã©ã ã®ã¿è¿œå
foreach ($additionalData as $column => $value) {
if (in_array($column, $existingColumns) && !in_array($column, $columns)) {
$columns[] = $isPgsql ? "\"{$column}\"" : "`{$column}`";
$placeholders[] = ":{$column}";
$updateColumns[] = $column;
$params[$column] = $value;
}
}
if ($isPgsql) {
// PostgreSQLçš: ON CONFLICT ... DO UPDATE
$updateClauses = [];
foreach ($updateColumns as $col) {
$quotedCol = in_array($col, ['service_name', 'device_type', 'device_name', 'login_ip', 'created_by', 'updated_by']) ||
preg_match('/^(username|password)\d+$/', $col)
? $col
: "\"{$col}\"";
$updateClauses[] = "{$quotedCol} = EXCLUDED.{$quotedCol}";
}
$sql = "
INSERT INTO device_info
(" . implode(", ", $columns) . ")
VALUES (" . implode(", ", $placeholders) . ")
ON CONFLICT (primary_key) DO UPDATE SET
" . implode(",\n ", $updateClauses) . ",
updated_at = CURRENT_TIMESTAMP
";
} else {
// MySQLçš: ON DUPLICATE KEY UPDATE
$updateClauses = [];
foreach ($updateColumns as $col) {
$quotedCol = in_array($col, ['service_name', 'device_type', 'device_name', 'login_ip', 'created_by', 'updated_by']) ||
preg_match('/^(username|password)\d+$/', $col)
? $col
: "`{$col}`";
$updateClauses[] = "{$quotedCol} = VALUES({$quotedCol})";
}
$sql = "
INSERT INTO device_info
(" . implode(", ", $columns) . ")
VALUES (" . implode(", ", $placeholders) . ")
ON DUPLICATE KEY UPDATE
" . implode(",\n ", $updateClauses) . ",
updated_at = CURRENT_TIMESTAMP
";
}
try {
$this->database->execute($sql, $params);
return true;
} catch (Exception $e) {
throw new Exception("è£
眮æ
å ±ã®æ¿å
¥ã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* åçããŒãã«ã«ããŒã¿ãæ¿å
¥ãŸãã¯æŽæ°
* @param string $tableName
* @param array $data
* @return bool
* @throws Exception
*/
public function insertOrUpdateDynamicData($tableName, $data) {
$tableName = sanitizeTableName($tableName);
$isPgsql = $this->database->getDbType() === 'pgsql';
if (empty($data)) {
return true;
}
// ã«ã©ã åãšãã¬ãŒã¹ãã«ããŒãæºå
$columns = [];
$placeholders = [];
$updateClauses = [];
$params = [];
$placeholderIndex = 0;
$primaryKeyColumn = null;
// é©åãªã¯ã©ãŒãæåãéžæ
$quote = $isPgsql ? '"' : '`';
foreach ($data as $key => $value) {
if ($placeholderIndex === 0) {
$primaryKeyColumn = $key; // æåã®ã«ã©ã ãäž»ããŒ
}
// ã«ã©ã åã¯æ¥æ¬èªã®ãŸãŸäœ¿çšïŒé©åãªã¯ã©ãŒãã§ãšã¹ã±ãŒãïŒ
$columns[] = "{$quote}{$key}{$quote}";
// ãã¬ãŒã¹ãã«ããŒåã¯è±æ°åã®ã¿ïŒparam0, param1, ...ïŒ
$placeholder = "param" . $placeholderIndex;
$placeholders[] = ":{$placeholder}";
$params[$placeholder] = $value;
// äž»ããŒä»¥å€ã®æŽæ°å¥ãäœæ
if ($key !== $primaryKeyColumn) {
if ($isPgsql) {
$updateClauses[] = "{$quote}{$key}{$quote} = EXCLUDED.{$quote}{$key}{$quote}";
} else {
$updateClauses[] = "{$quote}{$key}{$quote} = VALUES({$quote}{$key}{$quote})";
}
}
$placeholderIndex++;
}
if ($isPgsql) {
// PostgreSQLçš
$tableQuote = '"';
$sql = "
INSERT INTO {$tableQuote}{$tableName}{$tableQuote}
(" . implode(", ", $columns) . ")
VALUES (" . implode(", ", $placeholders) . ")
";
if (!empty($updateClauses)) {
$sql .= " ON CONFLICT ({$quote}{$primaryKeyColumn}{$quote}) DO UPDATE SET "
. implode(", ", $updateClauses)
. ", updated_at = CURRENT_TIMESTAMP";
}
} else {
// MySQLçš
$sql = "
INSERT INTO `{$tableName}`
(" . implode(", ", $columns) . ")
VALUES (" . implode(", ", $placeholders) . ")
";
if (!empty($updateClauses)) {
$sql .= " ON DUPLICATE KEY UPDATE " . implode(", ", $updateClauses) . ", updated_at = CURRENT_TIMESTAMP";
}
}
try {
error_log("Dynamic insert SQL: " . $sql);
error_log("Dynamic insert params: " . json_encode($params, JSON_UNESCAPED_UNICODE));
$this->database->execute($sql, $params);
return true;
} catch (Exception $e) {
error_log("Dynamic insert error: " . $e->getMessage());
error_log("SQL: " . $sql);
error_log("Params: " . json_encode($params, JSON_UNESCAPED_UNICODE));
throw new Exception("åçããŒãã« '{$tableName}' ãžã®ããŒã¿æ¿å
¥ã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* CSVããŒã¿ãäžæ¬åŠç
* @param CsvProcessor $csvProcessor
* @return array åŠççµæ
* @throws Exception
*/
public function processCsvData(CsvProcessor $csvProcessor) {
$results = [
'success' => false,
'device_info_count' => 0,
'dynamic_tables_created' => [],
'dynamic_data_count' => 0,
'columns_added' => [],
'errors' => []
];
$isPgsql = $this->database->getDbType() === 'pgsql';
try {
// PostgreSQLã®å Žåããšã©ãŒãã³ããªã³ã°ãæ¹å
if ($isPgsql) {
// åã¹ããããåå¥ã®ãã©ã³ã¶ã¯ã·ã§ã³ã§åŠç
error_log("PostgreSQL mode: Using individual transactions");
}
// ãã©ã³ã¶ã¯ã·ã§ã³éå§
$this->database->beginTransaction();
// è£
眮æ
å ±ããŒãã«ã®ååšç¢ºèªïŒDatabaseInitializerã§äœææžã¿ã®ã¯ãïŒ
if (!$this->deviceInfoTableExists()) {
error_log("Warning: device_info table not found. Creating now...");
$this->createDeviceInfoTable();
}
$data = $csvProcessor->getData();
$extendedColumns = $csvProcessor->getExtendedColumns();
// device_infoããŒãã«ã®æ¢åã«ã©ã ãååŸ
$deviceInfoExistingColumns = $this->getTableColumns('device_info');
// åçããŒãã«ããšã«å¿
èŠãªã«ã©ã ãå€å®
$dynamicTableColumns = [];
foreach ($data as $row) {
$tableName = $csvProcessor->generateTableName($row);
if (!isset($dynamicTableColumns[$tableName])) {
$dynamicTableColumns[$tableName] = [];
}
// åæ¡åŒµã«ã©ã ãdevice_infoãŸãã¯åçããŒãã«ã«æ¯ãåã
foreach ($extendedColumns as $column) {
// device_infoã«ååšããªãã«ã©ã ã®ã¿åçããŒãã«ãž
if (!in_array($column, $deviceInfoExistingColumns)) {
if (!in_array($column, $dynamicTableColumns[$tableName])) {
$dynamicTableColumns[$tableName][] = $column;
}
}
}
}
// åçããŒãã«ãäœæãŸãã¯ã«ã©ã 远å
foreach ($dynamicTableColumns as $tableName => $columns) {
if (!$this->dynamicTableExists($tableName)) {
// ããŒãã«æ°èŠäœæ
$primaryKeyColumn = $csvProcessor->generatePrimaryKeyColumnName($data[0]);
$this->createDynamicTable($tableName, $primaryKeyColumn, $columns);
$results['dynamic_tables_created'][] = $tableName;
} else {
// æ¢åããŒãã«ã«äžè¶³ã«ã©ã ã远å
$existingColumns = $this->getTableColumns($tableName);
foreach ($columns as $column) {
if (!in_array($column, $existingColumns)) {
$this->addColumnToDynamicTable($tableName, $column);
$results['columns_added'][] = "{$tableName}.{$column}";
}
}
}
}
// ãªã¬ãŒã·ã§ã³ããŒãã«ã®ååšç¢ºèªïŒDatabaseInitializerã§äœææžã¿ã®ã¯ãïŒ
if (!$this->relationTableExists()) {
error_log("Warning: service_device_type_relations table not found. Creating now...");
$this->createRelationTable();
}
// ãããŸã§ã®å€æŽãã³ãããïŒPostgreSQLã®å ŽåãããŒãã«å®çŸ©å€æŽã確å®ïŒ
if ($isPgsql) {
$this->database->commit();
error_log("Table structure changes committed");
} else {
// MySQLã®å Žåã¯1ã€ã®ãã©ã³ã¶ã¯ã·ã§ã³ã§åŠçãç¶ãã
}
// ããŒã¿ãåŠç
foreach ($data as $rowIndex => $row) {
// PostgreSQLã®å Žåã¯åè¡ããšã«æ°ãããã©ã³ã¶ã¯ã·ã§ã³ãéå§
if ($isPgsql) {
$this->database->beginTransaction();
error_log("Started new transaction for row {$rowIndex}");
}
error_log("Processing row {$rowIndex}: " . json_encode($row, JSON_UNESCAPED_UNICODE));
try {
// è£
眮æ
å ±ããŒãã«ã«æ¿å
¥ïŒæ¡åŒµã«ã©ã ãå«ãïŒ
$deviceInfo = $csvProcessor->convertToDeviceInfo($row);
error_log("Device info data: " . json_encode($deviceInfo, JSON_UNESCAPED_UNICODE));
// æ¡åŒµã«ã©ã ã®äžã§device_infoã«ååšããã«ã©ã ãæœåº
$additionalDeviceInfoData = [];
foreach ($extendedColumns as $column) {
if (in_array($column, $deviceInfoExistingColumns)) {
$additionalDeviceInfoData[$column] = isset($row[$column]) ? $row[$column] : null;
}
}
error_log("Additional device info data: " . json_encode($additionalDeviceInfoData, JSON_UNESCAPED_UNICODE));
$this->insertOrUpdateDeviceInfo($deviceInfo, $additionalDeviceInfoData);
error_log("Successfully inserted device_info for row {$rowIndex}");
$results['device_info_count']++;
} catch (Exception $e) {
error_log("Row {$rowIndex} - device_info insert error: " . $e->getMessage());
error_log("Stack trace: " . $e->getTraceAsString());
// PostgreSQLã®å Žåã¯ããŒã«ããã¯ããŠæ¬¡ã®è¡ãž
if ($isPgsql) {
$this->database->rollback();
error_log("Transaction rolled back for row {$rowIndex}");
}
throw new Exception("è¡" . ($rowIndex + 2) . "ã®device_infoç»é²ã§ãšã©ãŒ: " . $e->getMessage());
}
// ãµãŒãã¹åãšè£
眮皮å¥ã®ãªã¬ãŒã·ã§ã³ãç»é²
// PostgreSQLã®å Žåããã©ã³ã¶ã¯ã·ã§ã³ç®¡çãç°¡çŽ åããããã
// upload.phpåŽã§ãŸãšããŠç»é²ããã®ã§ããã§ã¯ã¹ããã
if (!$isPgsql) {
try {
$this->registerServiceDeviceTypeRelation(
$row['ãµãŒãã¹å'],
$row['è£
眮皮å¥'],
'CSVèªåç»é²'
);
error_log("Successfully registered relation for row {$rowIndex}");
} catch (Exception $e) {
// ãªã¬ãŒã·ã§ã³ç»é²ãšã©ãŒã¯ãã°ã«èšé²ãããåŠçã¯ç¶ç¶
error_log("ãªã¬ãŒã·ã§ã³ç»é²ãšã©ãŒ: " . $e->getMessage());
}
} else {
error_log("Skipping relation registration in PostgreSQL mode (will be done in upload.php)");
}
// åçããŒãã«ã«æ¿å
¥
try {
$tableName = $csvProcessor->generateTableName($row);
error_log("Dynamic table name: {$tableName}");
// åçããŒãã«çšã®ããŒã¿ãäœæ
$dynamicData = [];
$dynamicData[$csvProcessor->generatePrimaryKeyColumnName($row)] = $csvProcessor->generatePrimaryKey($row);
foreach ($extendedColumns as $column) {
// device_infoã«ååšããã«ã©ã ã¯device_infoã«ããã以å€ã¯åçããŒãã«ã«
if (!in_array($column, $deviceInfoExistingColumns)) {
// åçããŒãã«ã®ã«ã©ã ãšããŠç»é²
$dynamicData[$column] = isset($row[$column]) ? $row[$column] : null;
}
}
error_log("Dynamic data: " . json_encode($dynamicData, JSON_UNESCAPED_UNICODE));
// åçããŒãã«ã«æ¿å
¥ïŒåçããŒãã«çšã®ããŒã¿ãååšããå Žåã®ã¿ïŒ
if (count($dynamicData) > 1) { // primary_key以å€ã®ã«ã©ã ãããå Žå
$this->insertOrUpdateDynamicData($tableName, $dynamicData);
error_log("Successfully inserted dynamic data for row {$rowIndex}");
$results['dynamic_data_count']++;
} else {
error_log("No dynamic data to insert for row {$rowIndex}");
}
} catch (Exception $e) {
error_log("Row {$rowIndex} - dynamic table insert error: " . $e->getMessage());
error_log("Stack trace: " . $e->getTraceAsString());
// PostgreSQLã®å Žåã¯ããŒã«ããã¯ããŠæ¬¡ã®è¡ãž
if ($isPgsql) {
$this->database->rollback();
error_log("Transaction rolled back for row {$rowIndex}");
}
throw new Exception("è¡" . ($rowIndex + 2) . "ã®åçããŒãã«ç»é²ã§ãšã©ãŒ: " . $e->getMessage());
}
// PostgreSQLã®å Žåã¯åè¡ã®åŠçåŸã«ã³ããã
if ($isPgsql) {
$this->database->commit();
error_log("Transaction committed for row {$rowIndex}");
}
}
// MySQLã®å Žåã®ã¿æçµã³ãããïŒPostgreSQLã¯åè¡ã§ã³ãããæžã¿ïŒ
if (!$isPgsql) {
$this->database->commit();
}
$results['success'] = true;
} catch (Exception $e) {
// ããŒã«ããã¯ïŒãã©ã³ã¶ã¯ã·ã§ã³ãã¢ã¯ãã£ããªå Žåã®ã¿ïŒ
try {
if ($this->database->inTransaction()) {
$this->database->rollBack();
error_log("Transaction rolled back due to error");
}
} catch (Exception $rollbackEx) {
error_log("Rollback failed: " . $rollbackEx->getMessage());
}
$results['errors'][] = $e->getMessage();
throw $e;
}
return $results;
}
/**
* è£
眮æ
å ±ãæ€çŽ¢
* @param string $serviceName
* @param string $deviceType
* @param int $limit
* @param int $offset
* @return array
*/
public function searchDevices($serviceName = null, $deviceType = null, $limit = 100, $offset = 0) {
$whereConditions = [];
$params = [];
if ($serviceName !== null && $serviceName !== '') {
$whereConditions[] = "service_name LIKE :service_name";
$params['service_name'] = '%' . $serviceName . '%';
}
if ($deviceType !== null && $deviceType !== '') {
$whereConditions[] = "device_type LIKE :device_type";
$params['device_type'] = '%' . $deviceType . '%';
}
$whereClause = !empty($whereConditions) ? 'WHERE ' . implode(' AND ', $whereConditions) : '';
$sql = "
SELECT * FROM device_info
{$whereClause}
ORDER BY service_name, device_type, device_name, username1
LIMIT :limit OFFSET :offset
";
$params['limit'] = $limit;
$params['offset'] = $offset;
try {
$stmt = $this->database->execute($sql, $params);
return $stmt->fetchAll();
} catch (Exception $e) {
throw new Exception("è£
眮æ
å ±ã®æ€çŽ¢ã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* è£
眮æ
å ±ã®ç·æ°ãååŸ
* @param string $serviceName
* @param string $deviceType
* @return int
*/
public function countDevices($serviceName = null, $deviceType = null) {
$whereConditions = [];
$params = [];
if ($serviceName !== null && $serviceName !== '') {
$whereConditions[] = "service_name LIKE :service_name";
$params['service_name'] = '%' . $serviceName . '%';
}
if ($deviceType !== null && $deviceType !== '') {
$whereConditions[] = "device_type LIKE :device_type";
$params['device_type'] = '%' . $deviceType . '%';
}
$whereClause = !empty($whereConditions) ? 'WHERE ' . implode(' AND ', $whereConditions) : '';
$sql = "SELECT COUNT(*) FROM device_info {$whereClause}";
try {
$stmt = $this->database->execute($sql, $params);
return (int)$stmt->fetchColumn();
} catch (Exception $e) {
throw new Exception("è£
眮æ°ã®ååŸã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* åçããŒãã«ã®äžèЧãååŸ
* @return array
*/
public function getDynamicTables() {
$sql = "
SELECT table_name
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name != 'device_info'
ORDER BY table_name
";
try {
$stmt = $this->database->execute($sql);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
} catch (Exception $e) {
throw new Exception("åçããŒãã«äžèЧã®ååŸã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* ãµãŒãã¹åã®äžèЧãååŸ
* @return array
*/
public function getServiceNames() {
$sql = "
SELECT DISTINCT service_name
FROM device_info
ORDER BY service_name
";
try {
$stmt = $this->database->execute($sql);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
} catch (Exception $e) {
throw new Exception("ãµãŒãã¹åäžèЧã®ååŸã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* æå®ãµãŒãã¹åã®è£
眮皮å¥äžèЧãååŸ
* @param string|null $serviceName
* @return array
*/
public function getDeviceTypes($serviceName = null) {
$sql = "
SELECT DISTINCT device_type
FROM device_info
";
$params = [];
if ($serviceName !== null && $serviceName !== '') {
$sql .= " WHERE service_name = :service_name";
$params['service_name'] = $serviceName;
}
$sql .= " ORDER BY device_type";
try {
$stmt = $this->database->execute($sql, $params);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
} catch (Exception $e) {
throw new Exception("è£
眮皮å¥äžèЧã®ååŸã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* è£
眮æ
å ±ã詳现æ€çŽ¢
* @param string|null $serviceName
* @param string|null $deviceType
* @param string|null $deviceName
* @param int $limit
* @param int $offset
* @return array
*/
public function searchDevicesAdvanced($serviceName = null, $deviceType = null, $deviceName = null, $limit = 50, $offset = 0) {
$whereConditions = [];
$params = [];
if ($serviceName !== null && $serviceName !== '') {
$whereConditions[] = "service_name = :service_name";
$params['service_name'] = $serviceName;
}
if ($deviceType !== null && $deviceType !== '') {
$whereConditions[] = "device_type = :device_type";
$params['device_type'] = $deviceType;
}
if ($deviceName !== null && $deviceName !== '') {
$whereConditions[] = "device_name LIKE :device_name";
$params['device_name'] = '%' . $deviceName . '%';
}
$whereClause = !empty($whereConditions) ? 'WHERE ' . implode(' AND ', $whereConditions) : '';
$sql = "
SELECT
primary_key,
service_name,
device_type,
device_name,
login_ip,
username1,
password1,
username2,
password2,
username3,
password3,
username4,
password4,
username5,
password5,
username6,
password6,
username7,
password7,
username8,
password8,
username9,
password9,
username10,
password10,
created_by,
updated_by,
created_at,
updated_at
FROM device_info
{$whereClause}
ORDER BY service_name, device_type, device_name, username1
LIMIT :limit OFFSET :offset
";
$params['limit'] = $limit;
$params['offset'] = $offset;
try {
$stmt = $this->database->execute($sql, $params);
return $stmt->fetchAll();
} catch (Exception $e) {
throw new Exception("è£
眮æ
å ±ã®è©³çްæ€çŽ¢ã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* 詳现æ€çŽ¢ã®çµæä»¶æ°ãååŸ
* @param string|null $serviceName
* @param string|null $deviceType
* @param string|null $deviceName
* @return int
*/
public function countDevicesAdvanced($serviceName = null, $deviceType = null, $deviceName = null) {
$whereConditions = [];
$params = [];
if ($serviceName !== null && $serviceName !== '') {
$whereConditions[] = "service_name = :service_name";
$params['service_name'] = $serviceName;
}
if ($deviceType !== null && $deviceType !== '') {
$whereConditions[] = "device_type = :device_type";
$params['device_type'] = $deviceType;
}
if ($deviceName !== null && $deviceName !== '') {
$whereConditions[] = "device_name LIKE :device_name";
$params['device_name'] = '%' . $deviceName . '%';
}
$whereClause = !empty($whereConditions) ? 'WHERE ' . implode(' AND ', $whereConditions) : '';
$sql = "SELECT COUNT(*) FROM device_info {$whereClause}";
try {
$stmt = $this->database->execute($sql, $params);
return (int)$stmt->fetchColumn();
} catch (Exception $e) {
throw new Exception("è£
眮æ°ã®ååŸã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* è£
çœ®çµ±èšæ
å ±ãååŸ
* @return array
*/
public function getDeviceStatistics() {
try {
$statistics = [
'total_devices' => 0,
'total_services' => 0,
'total_device_types' => 0,
'total_combinations' => 0,
'total_relations' => 0
];
// device_infoããŒãã«ãååšããå Žåã®çµ±èš
if ($this->database->tableExists('device_info')) {
$sql = "
SELECT
COUNT(*) as total_devices,
COUNT(DISTINCT service_name) as total_services,
COUNT(DISTINCT device_type) as total_device_types,
COUNT(DISTINCT CONCAT(service_name, '_', device_type)) as total_combinations
FROM device_info
";
$stmt = $this->database->execute($sql);
$result = $stmt->fetch();
$statistics['total_devices'] = (int)$result['total_devices'];
$statistics['total_services'] = (int)$result['total_services'];
$statistics['total_device_types'] = (int)$result['total_device_types'];
$statistics['total_combinations'] = (int)$result['total_combinations'];
}
// ãªã¬ãŒã·ã§ã³æ°
if ($this->relationTableExists()) {
$sql = "SELECT COUNT(*) as count FROM service_device_type_relations WHERE is_active = 1";
$stmt = $this->database->execute($sql);
$result = $stmt->fetch();
$statistics['total_relations'] = (int)$result['count'];
}
return $statistics;
} catch (Exception $e) {
error_log("Get statistics error: " . $e->getMessage());
return [
'total_devices' => 0,
'total_services' => 0,
'total_device_types' => 0,
'total_combinations' => 0,
'total_relations' => 0
];
}
}
/**
* ãªã¬ãŒã·ã§ã³ããŒãã«ãååšããããã§ãã¯
* @return bool
*/
public function relationTableExists() {
return $this->database->tableExists('service_device_type_relations');
}
/**
* ãªã¬ãŒã·ã§ã³ããŒãã«ãäœæ
* @return bool
* @throws Exception
*/
public function createRelationTable() {
$isPgsql = $this->database->getDbType() === 'pgsql';
if ($isPgsql) {
// PostgreSQLçšSQL
$sql = "
CREATE TABLE IF NOT EXISTS service_device_type_relations (
id SERIAL PRIMARY KEY,
service_name VARCHAR(100) NOT NULL,
device_type VARCHAR(100) NOT NULL,
description TEXT,
is_active SMALLINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (service_name, device_type)
)
";
$this->database->execute($sql);
// ã€ã³ããã¯ã¹äœæ
$this->database->execute("CREATE INDEX IF NOT EXISTS idx_service_name ON service_device_type_relations (service_name)");
$this->database->execute("CREATE INDEX IF NOT EXISTS idx_device_type ON service_device_type_relations (device_type)");
$this->database->execute("CREATE INDEX IF NOT EXISTS idx_active ON service_device_type_relations (is_active)");
} else {
// MySQLçšSQL
$sql = "
CREATE TABLE IF NOT EXISTS service_device_type_relations (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ID',
service_name VARCHAR(100) NOT NULL COMMENT 'ãµãŒãã¹å',
device_type VARCHAR(100) NOT NULL COMMENT 'è£
眮皮å¥',
description TEXT COMMENT '説æ',
is_active TINYINT(1) DEFAULT 1 COMMENT 'æå¹ãã©ã°(1:æå¹, 0:ç¡å¹)',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'äœææ¥æ',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æŽæ°æ¥æ',
UNIQUE KEY unique_service_device_type (service_name, device_type),
INDEX idx_service_name (service_name),
INDEX idx_device_type (device_type),
INDEX idx_active (is_active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='ãµãŒãã¹åãšè£
眮皮å¥ã®ãªã¬ãŒã·ã§ã³ããŒãã«'
";
$this->database->execute($sql);
}
return true;
}
/**
* ãµãŒãã¹åãšè£
眮皮å¥ã®ãªã¬ãŒã·ã§ã³ãç»é²
* @param string $serviceName
* @param string $deviceType
* @param string $description
* @return bool
* @throws Exception
*/
public function registerServiceDeviceTypeRelation($serviceName, $deviceType, $description = null) {
// ãªã¬ãŒã·ã§ã³ããŒãã«ãååšããªãå Žåã¯äœæ
if (!$this->relationTableExists()) {
$this->createRelationTable();
}
$isPgsql = $this->database->getDbType() === 'pgsql';
if ($isPgsql) {
// PostgreSQLçš
$sql = "
INSERT INTO service_device_type_relations
(service_name, device_type, description)
VALUES (:service_name, :device_type, :description)
ON CONFLICT (service_name, device_type)
DO UPDATE SET
description = EXCLUDED.description,
is_active = TRUE,
updated_at = CURRENT_TIMESTAMP
";
} else {
// MySQLçš
$sql = "
INSERT INTO service_device_type_relations
(service_name, device_type, description)
VALUES (:service_name, :device_type, :description)
ON DUPLICATE KEY UPDATE
description = VALUES(description),
is_active = 1,
updated_at = CURRENT_TIMESTAMP
";
}
$params = [
'service_name' => $serviceName,
'device_type' => $deviceType,
'description' => $description
];
try {
$this->database->execute($sql, $params);
return true;
} catch (Exception $e) {
error_log("Relation registration error: " . $e->getMessage());
error_log("SQL: " . $sql);
error_log("Params: " . json_encode($params, JSON_UNESCAPED_UNICODE));
throw new Exception("ãªã¬ãŒã·ã§ã³ç»é²ã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* æå®ãµãŒãã¹åã®è£
眮皮å¥äžèЧããªã¬ãŒã·ã§ã³ããŒãã«ããååŸ
* @param string|null $serviceName
* @return array
*/
public function getDeviceTypesFromRelation($serviceName = null) {
// ãªã¬ãŒã·ã§ã³ããŒãã«ãååšããªãå Žåã¯åŸæ¥ã®æ¹æ³
if (!$this->relationTableExists()) {
return $this->getDeviceTypes($serviceName);
}
$sql = "
SELECT DISTINCT device_type
FROM service_device_type_relations
WHERE is_active = 1
";
$params = [];
if ($serviceName !== null && $serviceName !== '') {
$sql .= " AND service_name = :service_name";
$params['service_name'] = $serviceName;
}
$sql .= " ORDER BY device_type";
try {
$stmt = $this->database->execute($sql, $params);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
} catch (Exception $e) {
throw new Exception("è£
眮皮å¥äžèЧã®ååŸã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* ãªã¬ãŒã·ã§ã³ããŒãã«ããå
šãµãŒãã¹åãååŸ
* @return array
*/
public function getServiceNamesFromRelation() {
// ãªã¬ãŒã·ã§ã³ããŒãã«ãååšããªãå Žåã¯åŸæ¥ã®æ¹æ³
if (!$this->relationTableExists()) {
return $this->getServiceNames();
}
$sql = "
SELECT DISTINCT service_name
FROM service_device_type_relations
WHERE is_active = 1
ORDER BY service_name
";
try {
$stmt = $this->database->execute($sql);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
} catch (Exception $e) {
throw new Exception("ãµãŒãã¹åäžèЧã®ååŸã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* ãªã¬ãŒã·ã§ã³ã®ååšãã§ãã¯
* @param string $serviceName
* @param string $deviceType
* @return bool
*/
public function checkRelationExists($serviceName, $deviceType) {
// ãªã¬ãŒã·ã§ã³ããŒãã«ãååšããªãå Žåã¯åžžã«trueãè¿ã
if (!$this->relationTableExists()) {
return true;
}
$sql = "
SELECT COUNT(*)
FROM service_device_type_relations
WHERE service_name = :service_name
AND device_type = :device_type
AND is_active = 1
";
$params = [
'service_name' => $serviceName,
'device_type' => $deviceType
];
try {
$stmt = $this->database->execute($sql, $params);
return $stmt->fetchColumn() > 0;
} catch (Exception $e) {
throw new Exception("ãªã¬ãŒã·ã§ã³ååšç¢ºèªã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* æ¢åã®device_infoãããªã¬ãŒã·ã§ã³ãèªåæ§ç¯
* @return array æ§ç¯çµæ
* @throws Exception
*/
public function buildRelationsFromExistingData() {
// ãªã¬ãŒã·ã§ã³ããŒãã«ã®äœæ
if (!$this->relationTableExists()) {
$this->createRelationTable();
}
// æ¢åã®ãµãŒãã¹åãšè£
眮皮å¥ã®çµã¿åãããååŸ
$sql = "
SELECT DISTINCT
service_name,
device_type,
COUNT(*) as device_count
FROM device_info
GROUP BY service_name, device_type
ORDER BY service_name, device_type
";
try {
$stmt = $this->database->execute($sql);
$combinations = $stmt->fetchAll();
$registered = 0;
$errors = [];
foreach ($combinations as $combination) {
try {
$description = "è£
眮æ°: {$combination['device_count']}å° (èªåæ§ç¯)";
$this->registerServiceDeviceTypeRelation(
$combination['service_name'],
$combination['device_type'],
$description
);
$registered++;
} catch (Exception $e) {
$errors[] = "{$combination['service_name']} - {$combination['device_type']}: " . $e->getMessage();
}
}
return [
'success' => true,
'registered' => $registered,
'total_combinations' => count($combinations),
'errors' => $errors
];
} catch (Exception $e) {
throw new Exception("ãªã¬ãŒã·ã§ã³èªåæ§ç¯ã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* å
šãªã¬ãŒã·ã§ã³äžèЧãååŸïŒç®¡ççšïŒ
* @return array
*/
public function getAllRelations() {
if (!$this->relationTableExists()) {
return [];
}
$sql = "
SELECT
id,
service_name,
device_type,
description,
is_active,
created_at,
updated_at
FROM service_device_type_relations
ORDER BY service_name, device_type
";
try {
$stmt = $this->database->execute($sql);
return $stmt->fetchAll();
} catch (Exception $e) {
throw new Exception("ãªã¬ãŒã·ã§ã³äžèЧã®ååŸã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* ããŒãã«ã®ã«ã©ã äžèЧãååŸ
* @param string $tableName
* @return array
*/
public function getTableColumns($tableName) {
$isPgsql = $this->database->getDbType() === 'pgsql';
if ($isPgsql) {
$sql = "
SELECT column_name
FROM information_schema.columns
WHERE table_name = :table_name
ORDER BY ordinal_position
";
$params = ['table_name' => $tableName];
} else {
$sql = "
SELECT COLUMN_NAME as column_name
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = :table_name
ORDER BY ORDINAL_POSITION
";
$params = ['table_name' => $tableName];
}
try {
$stmt = $this->database->execute($sql, $params);
$columns = [];
while ($row = $stmt->fetch()) {
$columns[] = $row['column_name'];
}
return $columns;
} catch (Exception $e) {
error_log("Failed to get columns for table {$tableName}: " . $e->getMessage());
return [];
}
}
/**
* åçããŒãã«ã«ã«ã©ã ã远å
* @param string $tableName
* @param string $columnName
* @return bool
* @throws Exception
*/
public function addColumnToDynamicTable($tableName, $columnName) {
$tableName = sanitizeTableName($tableName);
$isPgsql = $this->database->getDbType() === 'pgsql';
if ($isPgsql) {
$sql = "ALTER TABLE \"{$tableName}\" ADD COLUMN \"{$columnName}\" TEXT";
} else {
$sql = "ALTER TABLE `{$tableName}` ADD COLUMN `{$columnName}` TEXT";
}
try {
error_log("Adding column to dynamic table: {$sql}");
$this->database->execute($sql);
return true;
} catch (Exception $e) {
error_log("Failed to add column {$columnName} to table {$tableName}: " . $e->getMessage());
throw new Exception("ã«ã©ã '{$columnName}' ãããŒãã« '{$tableName}' ã«è¿œå ã§ããŸããã§ãã: " . $e->getMessage());
}
}
/**
* Primary Keyã§è£
眮æ
å ±ãååŸ
* @param string $primaryKey
* @return array|null
*/
public function getDeviceByPrimaryKey($primaryKey) {
$sql = "SELECT * FROM device_info WHERE primary_key = :primary_key";
try {
$stmt = $this->database->execute($sql, ['primary_key' => $primaryKey]);
$result = $stmt->fetch();
return $result ?: null;
} catch (Exception $e) {
throw new Exception("è£
眮æ
å ±ã®ååŸã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* è£
眮æ
å ±ãæŽæ°
* @param string $primaryKey
* @param array $deviceData
* @return bool
*/
public function updateDeviceInfo($primaryKey, $deviceData) {
$updateFields = [];
$params = ['primary_key' => $primaryKey];
// æŽæ°å¯èœãªãã£ãŒã«ã
$allowedFields = ['service_name', 'device_type', 'device_name', 'login_ip'];
for ($i = 1; $i <= 10; $i++) {
$allowedFields[] = "username{$i}";
$allowedFields[] = "password{$i}";
}
foreach ($deviceData as $field => $value) {
if (in_array($field, $allowedFields)) {
$updateFields[] = "{$field} = :{$field}";
$params[$field] = $value;
}
}
if (empty($updateFields)) {
throw new Exception("æŽæ°ãããã£ãŒã«ãããããŸãã");
}
// æŽæ°è
ã®æ
å ±ã远å
require_once __DIR__ . '/../includes/auth_helper.php';
$currentUser = getLoggedInUsername();
if ($currentUser) {
$updateFields[] = "updated_by = :updated_by";
$params['updated_by'] = $currentUser;
}
$sql = "UPDATE device_info SET " . implode(', ', $updateFields) . ", updated_at = CURRENT_TIMESTAMP WHERE primary_key = :primary_key";
try {
$this->database->execute($sql, $params);
return true;
} catch (Exception $e) {
throw new Exception("è£
眮æ
å ±ã®æŽæ°ã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* è£
眮æ
å ±ãåé€
* @param string $primaryKey
* @return bool
*/
public function deleteDeviceInfo($primaryKey) {
try {
// device_infoããåé€
$sql = "DELETE FROM device_info WHERE primary_key = :primary_key";
$this->database->execute($sql, ['primary_key' => $primaryKey]);
return true;
} catch (Exception $e) {
throw new Exception("è£
眮æ
å ±ã®åé€ã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* åçããŒãã«ãããé¢é£ããŒã¿ãåé€
* @param string $tableName
* @param string $primaryKey
* @return bool
*/
public function deleteFromDynamicTable($tableName, $primaryKey) {
$isPgsql = $this->database->getDbType() === 'pgsql';
try {
if ($isPgsql) {
$sql = "DELETE FROM \"{$tableName}\" WHERE primary_key = :primary_key";
} else {
$sql = "DELETE FROM `{$tableName}` WHERE primary_key = :primary_key";
}
$this->database->execute($sql, ['primary_key' => $primaryKey]);
return true;
} catch (Exception $e) {
throw new Exception("åçããŒãã«ããã®åé€ã«å€±æããŸãã: " . $e->getMessage());
}
}
/**
* åçããŒãã«ããããŒã¿ãååŸ
* @param string $tableName
* @param string $primaryKey
* @return array|null
*/
public function getDynamicTableData($tableName, $primaryKey) {
$isPgsql = $this->database->getDbType() === 'pgsql';
try {
if ($isPgsql) {
$sql = "SELECT * FROM \"{$tableName}\" WHERE primary_key = :primary_key";
} else {
$sql = "SELECT * FROM `{$tableName}` WHERE primary_key = :primary_key";
}
$stmt = $this->database->execute($sql, ['primary_key' => $primaryKey]);
return $stmt->fetch();
} catch (Exception $e) {
error_log("åçããŒãã« '{$tableName}' ããã®ããŒã¿ååŸã«å€±æ: " . $e->getMessage());
return null;
}
}
/**
* åçããŒãã«ã®æ¡åŒµååãååŸïŒåºæ¬åãé€ãïŒ
* @param string $tableName
* @return array
*/
public function getDynamicTableExtendedColumns($tableName) {
$allColumns = $this->getTableColumns($tableName);
// åºæ¬åïŒdevice_infoãšå
±éã®åïŒãé€å€
$baseColumns = [
'primary_key', 'device_name', 'login_ip',
'username1', 'password1', 'username2', 'password2',
'username3', 'password3', 'username4', 'password4',
'username5', 'password5', 'username6', 'password6',
'username7', 'password7', 'username8', 'password8',
'username9', 'password9', 'username10', 'password10',
'created_at', 'updated_at'
];
$extendedColumns = [];
foreach ($allColumns as $column) {
if (!in_array($column, $baseColumns)) {
$extendedColumns[] = $column;
}
}
return $extendedColumns;
}
}
?>
<?php
/**
* ããŒã¿ããŒã¹åæåã¯ã©ã¹
* å¿
èŠãªããŒãã«ã®ååšç¢ºèªãšèªåäœæãè¡ã
*/
class DatabaseInitializer {
private $database;
private $isPgsql;
public function __construct(Database $database) {
$this->database = $database;
$this->isPgsql = $database->getDbType() === 'pgsql';
}
/**
* ãã¹ãŠã®å¿
é ããŒãã«ãåæå
* @return array åæåçµæ
* @throws Exception
*/
public function initializeAllTables() {
$results = [
'success' => false,
'tables_created' => [],
'errors' => []
];
try {
error_log("Database initialization started");
// device_infoããŒãã«ã®åæå
if ($this->initializeDeviceInfoTable()) {
$results['tables_created'][] = 'device_info';
error_log("device_info table initialized");
}
// service_device_type_relationsããŒãã«ã®åæå
if ($this->initializeRelationTable()) {
$results['tables_created'][] = 'service_device_type_relations';
error_log("service_device_type_relations table initialized");
}
// created_byãupdated_byã«ã©ã ã®ãã€ã°ã¬ãŒã·ã§ã³
$this->migrateCreatedUpdatedByColumns();
$results['success'] = true;
error_log("Database initialization completed successfully");
} catch (Exception $e) {
$results['errors'][] = $e->getMessage();
error_log("Database initialization error: " . $e->getMessage());
throw $e;
}
return $results;
}
/**
* device_infoããŒãã«ã®åæå
* @return bool ããŒãã«ãæ°èŠäœæããå Žåtrue
* @throws Exception
*/
private function initializeDeviceInfoTable() {
if ($this->database->tableExists('device_info')) {
error_log("device_info table already exists");
return false;
}
error_log("Creating device_info table");
if ($this->isPgsql) {
// PostgreSQLçš
$sql = "
CREATE TABLE IF NOT EXISTS device_info (
primary_key VARCHAR(500) NOT NULL PRIMARY KEY,
service_name VARCHAR(100) NOT NULL,
device_type VARCHAR(100) NOT NULL,
device_name VARCHAR(100) NOT NULL,
login_ip VARCHAR(45),
username1 VARCHAR(100) NOT NULL,
password1 VARCHAR(255),
username2 VARCHAR(100),
password2 VARCHAR(255),
username3 VARCHAR(100),
password3 VARCHAR(255),
username4 VARCHAR(100),
password4 VARCHAR(255),
username5 VARCHAR(100),
password5 VARCHAR(255),
username6 VARCHAR(100),
password6 VARCHAR(255),
username7 VARCHAR(100),
password7 VARCHAR(255),
username8 VARCHAR(100),
password8 VARCHAR(255),
username9 VARCHAR(100),
password9 VARCHAR(255),
username10 VARCHAR(100),
password10 VARCHAR(255),
created_by VARCHAR(100),
updated_by VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
";
$this->database->execute($sql);
// ã€ã³ããã¯ã¹äœæ
$this->database->execute("CREATE INDEX IF NOT EXISTS idx_service_device_type ON device_info (service_name, device_type)");
$this->database->execute("CREATE INDEX IF NOT EXISTS idx_device_info ON device_info (service_name, device_type, device_name, username1)");
} else {
// MySQLçš
$sql = "
CREATE TABLE IF NOT EXISTS device_info (
primary_key VARCHAR(500) NOT NULL PRIMARY KEY COMMENT 'ãµãŒãã¹å_è£
眮皮å¥å_è£
眮å_ãŠãŒã¶åã®è€åããŒ',
service_name VARCHAR(100) NOT NULL COMMENT 'ãµãŒãã¹å',
device_type VARCHAR(100) NOT NULL COMMENT 'è£
眮皮å¥',
device_name VARCHAR(100) NOT NULL COMMENT 'è£
眮åç§°',
login_ip VARCHAR(45) COMMENT 'ãã°ã€ã³IP',
username1 VARCHAR(100) NOT NULL COMMENT 'ãŠãŒã¶ãŒå1',
password1 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã1',
username2 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå2',
password2 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã2',
username3 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå3',
password3 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã3',
username4 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå4',
password4 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã4',
username5 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå5',
password5 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã5',
username6 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå6',
password6 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã6',
username7 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå7',
password7 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã7',
username8 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå8',
password8 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã8',
username9 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå9',
password9 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã9',
username10 VARCHAR(100) COMMENT 'ãŠãŒã¶ãŒå10',
password10 VARCHAR(255) COMMENT 'ãã¹ã¯ãŒã10',
created_by VARCHAR(100) COMMENT 'äœæè
',
updated_by VARCHAR(100) COMMENT 'æŽæ°è
',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'äœææ¥æ',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æŽæ°æ¥æ',
INDEX idx_service_device_type (service_name, device_type),
INDEX idx_device_info (service_name, device_type, device_name, username1)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='è£
çœ®åºæ¬æ
å ±ããŒãã«'
";
$this->database->execute($sql);
}
error_log("device_info table created successfully");
return true;
}
/**
* service_device_type_relationsããŒãã«ã®åæå
* @return bool ããŒãã«ãæ°èŠäœæããå Žåtrue
* @throws Exception
*/
private function initializeRelationTable() {
if ($this->database->tableExists('service_device_type_relations')) {
error_log("service_device_type_relations table already exists");
return false;
}
error_log("Creating service_device_type_relations table");
if ($this->isPgsql) {
// PostgreSQLçš
$sql = "
CREATE TABLE IF NOT EXISTS service_device_type_relations (
id SERIAL PRIMARY KEY,
service_name VARCHAR(100) NOT NULL,
device_type VARCHAR(100) NOT NULL,
description TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (service_name, device_type)
)
";
$this->database->execute($sql);
// ã€ã³ããã¯ã¹äœæ
$this->database->execute("CREATE INDEX IF NOT EXISTS idx_service_name ON service_device_type_relations (service_name)");
$this->database->execute("CREATE INDEX IF NOT EXISTS idx_device_type ON service_device_type_relations (device_type)");
} else {
// MySQLçš
$sql = "
CREATE TABLE IF NOT EXISTS service_device_type_relations (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ãªã¬ãŒã·ã§ã³ID',
service_name VARCHAR(100) NOT NULL COMMENT 'ãµãŒãã¹å',
device_type VARCHAR(100) NOT NULL COMMENT 'è£
眮皮å¥',
description TEXT COMMENT '説æ',
is_active BOOLEAN DEFAULT TRUE COMMENT 'ã¢ã¯ãã£ããã©ã°',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'äœææ¥æ',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æŽæ°æ¥æ',
UNIQUE KEY unique_service_device (service_name, device_type),
INDEX idx_service_name (service_name),
INDEX idx_device_type (device_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='ãµãŒãã¹-è£
眮皮å¥ãªã¬ãŒã·ã§ã³ããŒãã«'
";
$this->database->execute($sql);
}
error_log("service_device_type_relations table created successfully");
return true;
}
/**
* ããŒãã«ã®ååšç¢ºèª
* @param string $tableName
* @return bool
*/
public function tableExists($tableName) {
return $this->database->tableExists($tableName);
}
/**
* å¿
é ããŒãã«ããã¹ãŠååšããã確èª
* @return array åããŒãã«ã®ååšç¶æ³
*/
public function checkRequiredTables() {
return [
'device_info' => $this->database->tableExists('device_info'),
'service_device_type_relations' => $this->database->tableExists('service_device_type_relations')
];
}
/**
* device_infoããŒãã«ã«created_byãupdated_byã«ã©ã ã远å ãããã€ã°ã¬ãŒã·ã§ã³
* @return void
*/
private function migrateCreatedUpdatedByColumns() {
try {
// device_infoããŒãã«ãååšããªãå Žåã¯ã¹ããã
if (!$this->database->tableExists('device_info')) {
return;
}
// æ¢åã®ã«ã©ã ã確èª
if ($this->isPgsql) {
$checkSql = "
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'device_info'
AND column_name IN ('created_by', 'updated_by')
";
} else {
$dbName = $this->database->query("SELECT DATABASE()")[0]['DATABASE()'];
$checkSql = "
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'device_info'
AND TABLE_SCHEMA = '{$dbName}'
AND COLUMN_NAME IN ('created_by', 'updated_by')
";
}
$existingColumns = $this->database->query($checkSql);
$existingColumnNames = array_column($existingColumns, $this->isPgsql ? 'column_name' : 'COLUMN_NAME');
// created_byã«ã©ã ã远å
if (!in_array('created_by', $existingColumnNames)) {
error_log("Adding created_by column to device_info table");
if ($this->isPgsql) {
$sql = "ALTER TABLE device_info ADD COLUMN created_by VARCHAR(100)";
} else {
$sql = "ALTER TABLE device_info ADD COLUMN created_by VARCHAR(100) COMMENT 'äœæè
' AFTER password10";
}
$this->database->execute($sql);
error_log("created_by column added successfully");
}
// updated_byã«ã©ã ã远å
if (!in_array('updated_by', $existingColumnNames)) {
error_log("Adding updated_by column to device_info table");
if ($this->isPgsql) {
$sql = "ALTER TABLE device_info ADD COLUMN updated_by VARCHAR(100)";
} else {
$sql = "ALTER TABLE device_info ADD COLUMN updated_by VARCHAR(100) COMMENT 'æŽæ°è
' AFTER created_by";
}
$this->database->execute($sql);
error_log("updated_by column added successfully");
}
} catch (Exception $e) {
error_log("Migration error for created_by/updated_by columns: " . $e->getMessage());
// ãã€ã°ã¬ãŒã·ã§ã³ãšã©ãŒã¯èŠåãšããŠæ±ããåŠçãç¶ç¶
}
}
}
?>
#!/bin/bash
# MySQL ããã¯ã¢ããã¹ã¯ãªãã
# äœ¿çšæ¹æ³: ./backup_mysql.sh [daily|weekly|monthly]
# èšå®
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BACKUP_ROOT="${SCRIPT_DIR}/backups"
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-3306}"
DB_NAME="${DB_NAME:-device_management}"
DB_USER="${DB_USER:-root}"
DB_PASS="${DB_PASS:-}"
# ããã¯ã¢ããã¿ã€ãïŒåŒæ°ããååŸãããã©ã«ãã¯dailyïŒ
BACKUP_TYPE="${1:-daily}"
# ããã¯ã¢ãããã£ã¬ã¯ããª
BACKUP_DIR="${BACKUP_ROOT}/${BACKUP_TYPE}"
mkdir -p "${BACKUP_DIR}"
# ã¿ã€ã ã¹ã¿ã³ã
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_FILE="${BACKUP_DIR}/backup_${DB_NAME}_${TIMESTAMP}.sql.gz"
# ãã°ãã¡ã€ã«
LOG_DIR="${SCRIPT_DIR}/logs"
mkdir -p "${LOG_DIR}"
LOG_FILE="${LOG_DIR}/backup.log"
# ãã°é¢æ°
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}
log "=========================================="
log "ããã¯ã¢ããéå§: ${BACKUP_TYPE}"
log "ããŒã¿ããŒã¹: ${DB_NAME}"
# mysqldumpã®å®è¡
if [ -z "${DB_PASS}" ]; then
# ãã¹ã¯ãŒããªã
mysqldump -h "${DB_HOST}" \
-P "${DB_PORT}" \
-u "${DB_USER}" \
--single-transaction \
--routines \
--triggers \
--events \
"${DB_NAME}" | gzip > "${BACKUP_FILE}"
else
# ãã¹ã¯ãŒããã
mysqldump -h "${DB_HOST}" \
-P "${DB_PORT}" \
-u "${DB_USER}" \
-p"${DB_PASS}" \
--single-transaction \
--routines \
--triggers \
--events \
"${DB_NAME}" | gzip > "${BACKUP_FILE}"
fi
# ããã¯ã¢ããã®æåŠã確èª
if [ $? -eq 0 ] && [ -f "${BACKUP_FILE}" ]; then
BACKUP_SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
log "ããã¯ã¢ããæå: ${BACKUP_FILE} (${BACKUP_SIZE})"
else
log "ãšã©ãŒ: ããã¯ã¢ããã«å€±æããŸãã"
exit 1
fi
# å€ãããã¯ã¢ãããã¡ã€ã«ã®åé€
case "${BACKUP_TYPE}" in
daily)
# 7æ¥ä»¥äžåã®ãã¡ã€ã«ãåé€
KEEP_DAYS=7
log "æ¥æ¬¡ããã¯ã¢ãã: ${KEEP_DAYS}æ¥ããå€ããã¡ã€ã«ãåé€"
find "${BACKUP_DIR}" -name "backup_*.sql.gz" -type f -mtime +${KEEP_DAYS} -delete
;;
weekly)
# 4é±éïŒ28æ¥ïŒä»¥äžåã®ãã¡ã€ã«ãåé€
KEEP_DAYS=28
log "鱿¬¡ããã¯ã¢ãã: ${KEEP_DAYS}æ¥ããå€ããã¡ã€ã«ãåé€"
find "${BACKUP_DIR}" -name "backup_*.sql.gz" -type f -mtime +${KEEP_DAYS} -delete
;;
monthly)
# 12ã¶æïŒ365æ¥ïŒä»¥äžåã®ãã¡ã€ã«ãåé€
KEEP_DAYS=365
log "ææ¬¡ããã¯ã¢ãã: ${KEEP_DAYS}æ¥ããå€ããã¡ã€ã«ãåé€"
find "${BACKUP_DIR}" -name "backup_*.sql.gz" -type f -mtime +${KEEP_DAYS} -delete
;;
esac
# æ®ã£ãŠããããã¯ã¢ãããã¡ã€ã«ã®æ°ã衚瀺
BACKUP_COUNT=$(ls -1 "${BACKUP_DIR}"/backup_*.sql.gz 2>/dev/null | wc -l)
log "ä¿åãããŠããããã¯ã¢ãããã¡ã€ã«æ°: ${BACKUP_COUNT}"
log "ããã¯ã¢ããå®äº"
log "=========================================="
exit 0
<?php
/**
* ããŒã¿ããŒã¹ã¹ããŒã確èªããŒã«
*/
require_once 'config.php';
try {
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
echo "<h2>device_info ããŒãã«ã®ã«ã©ã 確èª</h2>\n";
if (!$database->tableExists('device_info')) {
echo "<p style='color: orange;'>device_info ããŒãã«ãååšããŸããã</p>\n";
echo "<p>CSVã¢ããããŒããå®è¡ãããšèªåçã«äœæãããŸãã</p>\n";
} else {
$columns = $database->getTableColumns('device_info');
echo "<h3>çŸåšã®ã«ã©ã äžèЧïŒ</h3>\n";
echo "<ul>\n";
foreach ($columns as $col) {
echo "<li>" . htmlspecialchars($col['COLUMN_NAME']) . " - " . htmlspecialchars($col['DATA_TYPE']) . "</li>\n";
}
echo "</ul>\n";
// å¿
é ã«ã©ã ã®ãã§ãã¯
$requiredColumns = ['primary_key', 'service_name', 'device_type', 'device_name', 'login_ip', 'username1', 'password1'];
$existingColumnNames = array_column($columns, 'COLUMN_NAME');
echo "<h3>å¿
é ã«ã©ã ã®ãã§ãã¯ïŒ</h3>\n";
$allOk = true;
foreach ($requiredColumns as $required) {
$exists = in_array($required, $existingColumnNames);
$color = $exists ? 'green' : 'red';
$status = $exists ? 'â ååš' : 'â äžè¶³';
echo "<p style='color: {$color};'>{$required}: {$status}</p>\n";
if (!$exists) {
$allOk = false;
}
}
// æ§ã«ã©ã ïŒåé€ãã¹ãïŒã®ãã§ãã¯
$oldColumns = ['device_ip', 'username', 'password'];
echo "<h3>æ§ã¹ããŒãã«ã©ã ã®ãã§ãã¯ïŒ</h3>\n";
$hasOldColumns = false;
foreach ($oldColumns as $old) {
if (in_array($old, $existingColumnNames)) {
echo "<p style='color: orange;'>â æ§ã«ã©ã ãæ®ã£ãŠããŸã: {$old}</p>\n";
$hasOldColumns = true;
}
}
if (!$hasOldColumns) {
echo "<p style='color: green;'>â æ§ã«ã©ã ã¯ååšããŸãã</p>\n";
}
// ç·åå€å®
echo "<hr>\n";
if ($allOk && !$hasOldColumns) {
echo "<h2 style='color: green;'>â ã¹ããŒãã¯æ£åžžã§ã</h2>\n";
// ããŒã¿ä»¶æ°ç¢ºèª
$stmt = $database->execute("SELECT COUNT(*) as count FROM device_info");
$result = $stmt->fetch();
echo "<p>ç»é²ããŒã¿ä»¶æ°: " . $result['count'] . " ä»¶</p>\n";
} elseif ($hasOldColumns) {
echo "<h2 style='color: orange;'>â æ§ã¹ããŒãã®ããŒãã«ãååšããŸã</h2>\n";
echo "<p><strong>å¯Ÿå¿æ¹æ³ïŒ</strong></p>\n";
echo "<ol>\n";
echo "<li>æ¢åããŒã¿ãäžèŠãªå Žå: ããŒãã«ãåé€ããŠåäœæ</li>\n";
echo "<li>æ¢åããŒã¿ãä¿æããå Žå: ãã€ã°ã¬ãŒã·ã§ã³ã¹ã¯ãªãããå®è¡</li>\n";
echo "</ol>\n";
echo "<p><a href='migrate_device_info.php'>â èªåãã€ã°ã¬ãŒã·ã§ã³ãå®è¡</a></p>\n";
} else {
echo "<h2 style='color: red;'>â å¿
é ã«ã©ã ãäžè¶³ããŠããŸã</h2>\n";
echo "<p>ããŒãã«ãåé€ããŠåäœæããŠãã ããã</p>\n";
}
}
$database->close();
} catch (Exception $e) {
echo "<p style='color: red;'>ãšã©ãŒ: " . htmlspecialchars($e->getMessage()) . "</p>\n";
}
?>
<?php
/**
* æ¢åããŒã¿ã®created_byãupdated_byãã£ãŒã«ãã«ããã©ã«ãå€ãèšå®ãããã€ã°ã¬ãŒã·ã§ã³
*/
require_once __DIR__ . '/../config.php';
try {
// ããŒã¿ããŒã¹æ¥ç¶
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
echo "ããŒã¿ããŒã¹ã«æ¥ç¶ããŸããã\n\n";
// created_byãNULLã®ã¬ã³ãŒãã確èª
$checkSql = "SELECT COUNT(*) as count FROM device_info WHERE created_by IS NULL OR updated_by IS NULL";
$stmt = $database->execute($checkSql);
$result = $stmt->fetch();
$nullCount = $result['count'];
echo "created_byãŸãã¯updated_byãNULLã®ã¬ã³ãŒãæ°: {$nullCount}\n\n";
if ($nullCount > 0) {
echo "æ¢åããŒã¿ã«ããã©ã«ãå€ãèšå®äž...\n";
// created_byãNULLã®å Žåã¯'system'ãèšå®
$updateCreatedBySql = "UPDATE device_info SET created_by = 'system' WHERE created_by IS NULL";
$database->execute($updateCreatedBySql);
echo "â created_byã«ããã©ã«ãå€ãèšå®ããŸããã\n";
// updated_byãNULLã®å Žåã¯'system'ãèšå®
$updateUpdatedBySql = "UPDATE device_info SET updated_by = 'system' WHERE updated_by IS NULL";
$database->execute($updateUpdatedBySql);
echo "â updated_byã«ããã©ã«ãå€ãèšå®ããŸããã\n\n";
echo "ãã€ã°ã¬ãŒã·ã§ã³å®äºïŒ\n";
} else {
echo "ãã¹ãŠã®ã¬ã³ãŒãã«å€ãèšå®ãããŠããŸããåŠçäžèŠã§ãã\n";
}
} catch (Exception $e) {
echo "ãšã©ãŒ: " . $e->getMessage() . "\n";
exit(1);
}
<?php
/**
* device_infoããŒãã«ã®ã¹ããŒã倿Žãã€ã°ã¬ãŒã·ã§ã³
* æ§: username, password, device_ip
* æ°: username1-10, password1-10, login_ip
*/
require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/../classes/Database.php';
try {
echo "ããŒã¿ããŒã¹æ¥ç¶äž...\n";
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_CHARSET, DB_TYPE, DB_PORT);
$database->connect();
echo "æ¢åã®device_infoããŒãã«ã確èªäž...\n";
$tableExists = $database->tableExists('device_info');
if (!$tableExists) {
echo "device_infoããŒãã«ãååšããŸãããæ°èŠäœæããŸãã\n";
require_once __DIR__ . '/../classes/DeviceManager.php';
$deviceManager = new DeviceManager($database);
$deviceManager->createDeviceInfoTable();
echo "â device_infoããŒãã«ãäœæããŸããã\n";
exit(0);
}
echo "æ¢åããŒã¿ã確èªäž...\n";
$stmt = $database->execute("SELECT COUNT(*) as cnt FROM device_info");
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$count = $result[0]['cnt'];
echo "æ¢åã¬ã³ãŒãæ°: {$count}\n";
if ($count > 0) {
echo "\nèŠå: æ¢åããŒã¿ãååšããŸãã\n";
echo "ãã®ãã€ã°ã¬ãŒã·ã§ã³ã§ã¯ãæ¢åããŒã¿ãä¿æãããŸãŸã¹ããŒãã倿ŽããŸãã\n";
echo "- username â username1\n";
echo "- password â password1\n";
echo "- device_ip â login_ip\n";
echo "- username2-10, password2-10 ã远å ïŒNULLïŒ\n\n";
// ããã¯ã¢ããããŒãã«äœæ
echo "ããã¯ã¢ããããŒãã«äœæäž...\n";
$timestamp = date('Ymd_His');
$backupTable = "device_info_backup_{$timestamp}";
$database->execute("CREATE TABLE {$backupTable} AS SELECT * FROM device_info");
echo "â ããã¯ã¢ããããŒãã«äœæå®äº: {$backupTable}\n";
}
echo "\nã¹ããŒã倿Žéå§...\n";
// 1. æ°ããã«ã©ã ã远å
echo "1. æ°ããã«ã©ã ã远å äž...\n";
$alterQueries = [
"ALTER TABLE device_info ADD COLUMN login_ip VARCHAR(45) AFTER device_name",
"ALTER TABLE device_info ADD COLUMN username1 VARCHAR(100) AFTER login_ip",
"ALTER TABLE device_info ADD COLUMN password1 VARCHAR(255) AFTER username1"
];
// username2-10, password2-10 ã远å
for ($i = 2; $i <= 10; $i++) {
$alterQueries[] = "ALTER TABLE device_info ADD COLUMN username{$i} VARCHAR(100) AFTER password" . ($i - 1);
$alterQueries[] = "ALTER TABLE device_info ADD COLUMN password{$i} VARCHAR(255) AFTER username{$i}";
}
foreach ($alterQueries as $query) {
try {
$database->execute($query);
} catch (Exception $e) {
// ã«ã©ã ãæ¢ã«ååšããå Žåã¯ã¹ããã
if (strpos($e->getMessage(), 'Duplicate column name') === false) {
throw $e;
}
}
}
echo "â æ°ããã«ã©ã ã远å ããŸããã\n";
if ($count > 0) {
// 2. ããŒã¿ãç§»è¡
echo "2. æ¢åããŒã¿ãæ°ã«ã©ã ã«ç§»è¡äž...\n";
$database->execute("UPDATE device_info SET username1 = username WHERE username1 IS NULL");
$database->execute("UPDATE device_info SET password1 = password WHERE password1 IS NULL");
$database->execute("UPDATE device_info SET login_ip = device_ip WHERE login_ip IS NULL");
echo "â ããŒã¿ç§»è¡å®äºã\n";
}
// 3. å€ãã«ã©ã ãåé€
echo "3. å€ãã«ã©ã ãåé€äž...\n";
try {
$database->execute("ALTER TABLE device_info DROP COLUMN username");
} catch (Exception $e) {
echo " - username ã«ã©ã ã¯æ¢ã«åé€ãããŠããŸãã\n";
}
try {
$database->execute("ALTER TABLE device_info DROP COLUMN password");
} catch (Exception $e) {
echo " - password ã«ã©ã ã¯æ¢ã«åé€ãããŠããŸãã\n";
}
try {
$database->execute("ALTER TABLE device_info DROP COLUMN device_ip");
} catch (Exception $e) {
echo " - device_ip ã«ã©ã ã¯æ¢ã«åé€ãããŠããŸãã\n";
}
echo "â å€ãã«ã©ã ãåé€ããŸããã\n";
// 4. username1 ã NOT NULL ã«å€æŽ
if ($count > 0) {
echo "4. username1 ã NOT NULL ã«å€æŽäž...\n";
$database->execute("ALTER TABLE device_info MODIFY username1 VARCHAR(100) NOT NULL COMMENT 'ãŠãŒã¶ãŒå1'");
echo "â username1 ã NOT NULL ã«å€æŽããŸããã\n";
}
// 5. ã€ã³ããã¯ã¹ãæŽæ°
echo "5. ã€ã³ããã¯ã¹ãæŽæ°äž...\n";
try {
$database->execute("DROP INDEX idx_device_info ON device_info");
} catch (Exception $e) {
echo " - idx_device_info ã¯æ¢ã«åé€ãããŠããŸãã\n";
}
$database->execute("CREATE INDEX idx_device_info ON device_info (service_name, device_type, device_name, username1)");
echo "â ã€ã³ããã¯ã¹ãæŽæ°ããŸããã\n";
echo "\nâ
ãã€ã°ã¬ãŒã·ã§ã³å®äºïŒ\n";
if ($count > 0) {
echo "\nããã¯ã¢ããããŒãã«: {$backupTable}\n";
echo "åé¡ããªããã°ã以äžã®ã³ãã³ãã§ããã¯ã¢ãããåé€ã§ããŸãïŒ\n";
echo "DROP TABLE {$backupTable};\n";
}
// æŽæ°åŸã®ã¹ããŒãã衚瀺
echo "\nçŸåšã®device_infoããŒãã«æ§é :\n";
$stmt = $database->execute("SHOW COLUMNS FROM device_info");
$columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($columns as $col) {
echo " - {$col['Field']}: {$col['Type']} " . ($col['Null'] === 'NO' ? 'NOT NULL' : 'NULL') . "\n";
}
} catch (Exception $e) {
echo "\nâ ãšã©ãŒ: " . $e->getMessage() . "\n";
exit(1);
}
?>
<?php
/**
* ããŒã¿ããŒã¹åæåã¹ã¯ãªããïŒãŠãŒã¶ãŒããŒãã«è¿œå ïŒ
*/
require_once __DIR__ . '/../config.php';
try {
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, DB_CHARSET, DB_TYPE, DB_PORT);
$conn = $database->connect();
echo "ããŒã¿ããŒã¹æ¥ç¶æå\n";
// ãŠãŒã¶ãŒããŒãã«äœæ
$sql = "CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ãŠãŒã¶ãŒID',
username VARCHAR(100) NOT NULL UNIQUE COMMENT 'ãŠãŒã¶ãŒå',
password_hash VARCHAR(255) NOT NULL COMMENT 'ãã¹ã¯ãŒãããã·ã¥ïŒbcryptïŒ',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'äœææ¥æ',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æŽæ°æ¥æ',
last_login TIMESTAMP NULL COMMENT 'æçµãã°ã€ã³æ¥æ',
is_active TINYINT(1) DEFAULT 1 COMMENT 'æå¹ãã©ã°(1:æå¹, 0:ç¡å¹)',
INDEX idx_username (username),
INDEX idx_active (is_active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='ãŠãŒã¶ãŒèªèšŒããŒãã«'";
$conn->exec($sql);
echo "â usersããŒãã«ãäœæããŸãã\n";
echo "\nåæåå®äºïŒ\n";
echo "ç»é²ããŒãžã«ã¢ã¯ã»ã¹ããŠãŠãŒã¶ãŒãäœæããŠãã ãã: register.php\n";
} catch (Exception $e) {
echo "ãšã©ãŒ: " . $e->getMessage() . "\n";
exit(1);
}
<?php
/**
* ããŒã¿ããŒã¹åæåã¹ã¯ãªãã
* ã¢ããªã±ãŒã·ã§ã³ã®ååã»ããã¢ããæã«å®è¡
*/
require_once __DIR__ . '/../config.php';
try {
echo "=== è£
眮æ
å ±ç®¡çã·ã¹ãã - ããŒã¿ããŒã¹åæå ===\n\n";
// ããŒã¿ããŒã¹æ¥ç¶ãã¹ã
echo "1. ããŒã¿ããŒã¹æ¥ç¶ãã¹ã...\n";
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
$pdo = $database->connect();
echo " â æ¥ç¶æå\n\n";
// ããŒã¿ããŒã¹åæåïŒå¿
é ããŒãã«ã®äœæïŒ
echo "2. å¿
é ããŒãã«äœæ...\n";
$dbInitializer = new DatabaseInitializer($database);
$initResults = $dbInitializer->initializeAllTables();
if (!empty($initResults['tables_created'])) {
foreach ($initResults['tables_created'] as $tableName) {
echo " â {$tableName} ããŒãã«ãäœæããŸãã\n";
}
} else {
echo " - ãã¹ãŠã®å¿
é ããŒãã«ã¯æ¢ã«ååšããŸã\n";
}
if (!empty($initResults['errors'])) {
foreach ($initResults['errors'] as $error) {
echo " â ãšã©ãŒ: {$error}\n";
}
}
// ããŒãã«ååšç¢ºèª
$tableStatus = $dbInitializer->checkRequiredTables();
echo "\n ããŒãã«ç¶æ
:\n";
foreach ($tableStatus as $tableName => $exists) {
$status = $exists ? "â ååš" : "â äžåš";
echo " - {$tableName}: {$status}\n";
}
// ããŒãã«æ
å ±ã®è¡šç€º
echo "\n3. ããŒãã«æ§é 確èª...\n";
$columns = $database->getTableColumns('device_info');
echo " device_info ããŒãã«ã®ã«ã©ã :\n";
foreach ($columns as $column) {
echo " - {$column['COLUMN_NAME']} ({$column['DATA_TYPE']})\n";
}
// ã¢ããããŒããã£ã¬ã¯ããªã®ç¢ºèª
echo "\n4. ã¢ããããŒããã£ã¬ã¯ããªç¢ºèª...\n";
if (!is_dir(UPLOAD_DIR)) {
mkdir(UPLOAD_DIR, 0755, true);
echo " â ã¢ããããŒããã£ã¬ã¯ããªãäœæããŸãã: " . UPLOAD_DIR . "\n";
} else {
echo " - ã¢ããããŒããã£ã¬ã¯ããªã¯æ¢ã«ååšããŸã: " . UPLOAD_DIR . "\n";
}
// æš©é確èª
if (is_writable(UPLOAD_DIR)) {
echo " â ã¢ããããŒããã£ã¬ã¯ããªã¯æžã蟌ã¿å¯èœã§ã\n";
} else {
echo " â ã¢ããããŒããã£ã¬ã¯ããªã«æžãèŸŒã¿æš©éããããŸãã\n";
}
// ãµã³ãã«CSVãã¡ã€ã«ã®ååšç¢ºèª
echo "\n5. ãµã³ãã«ãã¡ã€ã«ç¢ºèª...\n";
$sampleCsvPath = __DIR__ . '/sample.csv';
if (file_exists($sampleCsvPath)) {
echo " â ãµã³ãã«CSVãã¡ã€ã«ãååšããŸã: sample.csv\n";
// ãµã³ãã«ãã¡ã€ã«ã®å
容確èª
$csvProcessor = new CsvProcessor();
if ($csvProcessor->loadFile($sampleCsvPath)) {
$stats = $csvProcessor->getStatistics();
echo " - ããŒã¿è¡æ°: {$stats['total_rows']}\n";
echo " - ãµãŒãã¹: " . implode(', ', $stats['services']) . "\n";
echo " - è£
眮皮å¥: " . implode(', ', $stats['device_types']) . "\n";
}
} else {
echo " â sample.csv ãèŠã€ãããŸãã\n";
}
// èšå®å€ã®ç¢ºèª
echo "\n6. ã¢ããªã±ãŒã·ã§ã³èšå®ç¢ºèª...\n";
echo " - ããŒã¿ããŒã¹ãã¹ã: " . DB_HOST . "\n";
echo " - ããŒã¿ããŒã¹å: " . DB_NAME . "\n";
echo " - æå€§ã¢ããããŒããµã€ãº: " . (UPLOAD_MAX_SIZE / 1024 / 1024) . "MB\n";
echo " - èš±å¯ãã¡ã€ã«åœ¢åŒ: " . implode(', ', UPLOAD_ALLOWED_TYPES) . "\n";
echo "\n=== åæåå®äº ===\n";
echo "ãã©ãŠã¶ã§ index.php ã«ã¢ã¯ã»ã¹ããŠã¢ããªã±ãŒã·ã§ã³ãéå§ããŠãã ããã\n";
} catch (Exception $e) {
echo "â ãšã©ãŒãçºçããŸãã: " . $e->getMessage() . "\n";
echo "\nèšå®ã確èªããŠãã ãã:\n";
echo "1. MySQLãµãŒããŒãèµ·åããŠããããšã確èª\n";
echo "2. config.php ã®ããŒã¿ããŒã¹æ¥ç¶æ
å ±ã確èª\n";
echo "3. ããŒã¿ããŒã¹ '{DB_NAME}' ãååšããããšã確èª\n";
echo "4. ãŠãŒã¶ãŒ '{DB_USER}' ã«é©åãªæš©éãããããšã確èª\n";
}
?>
<?php
require_once '../config.php';
// 管çè
çšïŒããŒãã«ç®¡çããŒãž
ini_set('display_errors', 1);
error_reporting(E_ALL);
$message = '';
$error = '';
try {
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
$deviceManager = new DeviceManager($database);
// ããŒãã«åé€åŠç
if ($_POST['action'] ?? '' === 'delete_dynamic_tables') {
$tablesDeleted = 0;
// å
šããŒãã«ãååŸ
$sql = "SHOW TABLES";
$stmt = $database->execute($sql);
$tables = $stmt->fetchAll(PDO::FETCH_NUM);
foreach ($tables as $table) {
$tableName = $table[0];
// ã·ã¹ãã ããŒãã«ä»¥å€ãåé€
if (!in_array($tableName, ['device_info', 'service_device_type_relations'])) {
try {
$deleteSql = "DROP TABLE `{$tableName}`";
$database->execute($deleteSql);
$tablesDeleted++;
} catch (Exception $e) {
error_log("ããŒãã«åé€ãšã©ãŒ ({$tableName}): " . $e->getMessage());
}
}
}
$message = "{$tablesDeleted}åã®åçããŒãã«ãåé€ããŸããã";
}
// å
šããŒãã«ãååŸããŠè¡šç€º
$sql = "SHOW TABLES";
$stmt = $database->execute($sql);
$tables = $stmt->fetchAll(PDO::FETCH_NUM);
} catch (Exception $e) {
$error = "ãšã©ãŒ: " . $e->getMessage();
$tables = [];
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ããŒãã«ç®¡ç - è£
眮æ
å ±ç®¡çã·ã¹ãã </title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
border-bottom: 3px solid #dc3545;
padding-bottom: 15px;
}
.header h1 {
color: #333;
margin: 0;
}
.nav-buttons {
display: flex;
gap: 10px;
}
.nav-buttons a {
padding: 8px 16px;
background-color: #6c757d;
color: white;
text-decoration: none;
border-radius: 4px;
font-size: 14px;
transition: background-color 0.3s;
}
.nav-buttons a:hover {
background-color: #5a6268;
}
.alert {
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.alert-error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-warning {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: background-color 0.3s;
text-decoration: none;
display: inline-block;
margin-right: 10px;
}
.btn-danger {
background-color: #dc3545;
color: white;
}
.btn-danger:hover {
background-color: #c82333;
}
.table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.table th,
.table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.table th {
background-color: #f8f9fa;
font-weight: bold;
}
.table tr:hover {
background-color: #f8f9fa;
}
.system-table {
color: #28a745;
font-weight: bold;
}
.dynamic-table {
color: #dc3545;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>âïž ããŒãã«ç®¡ç</h1>
<div class="nav-buttons">
<a href="upload.php">ð€ CSVã¢ããããŒã</a>
<a href="manage.php">âïž è£
眮æ
å ±ç®¡ç</a>
<a href="relations.php">ð ãªã¬ãŒã·ã§ã³ç®¡ç</a>
</div>
</div>
<?php if ($error): ?>
<div class="alert alert-error">
<strong>ãšã©ãŒ:</strong> <?= htmlspecialchars($error) ?>
</div>
<?php endif; ?>
<?php if ($message): ?>
<div class="alert alert-success">
<strong>æå:</strong> <?= htmlspecialchars($message) ?>
</div>
<?php endif; ?>
<div class="alert alert-warning">
<h4>â ïž æ³šæ</h4>
<p>
ãã®ããŒãžã¯éçºã»ãã¹ãçšã§ããåçããŒãã«ãåé€ãããšãCSVã§ã¢ããããŒãããããŒã¿ã倱ãããŸãã
æ¬çªç°å¢ã§ã¯äœ¿çšããªãã§ãã ããã
</p>
</div>
<h3>çŸåšã®ããŒãã«äžèЧ</h3>
<table class="table">
<thead>
<tr>
<th>ããŒãã«å</th>
<th>ã¿ã€ã</th>
<th>説æ</th>
</tr>
</thead>
<tbody>
<?php foreach ($tables as $table): ?>
<?php
$tableName = $table[0];
$isSystemTable = in_array($tableName, ['device_info', 'service_device_type_relations']);
?>
<tr>
<td class="<?= $isSystemTable ? 'system-table' : 'dynamic-table' ?>">
<?= htmlspecialchars($tableName) ?>
</td>
<td>
<?= $isSystemTable ? 'ã·ã¹ãã ããŒãã«' : 'åçããŒãã«' ?>
</td>
<td>
<?php if ($tableName === 'device_info'): ?>
è£
眮æ
å ±ã®åºæ¬ããŒã¿
<?php elseif ($tableName === 'service_device_type_relations'): ?>
ãµãŒãã¹ã»è£
眮皮å¥ãªã¬ãŒã·ã§ã³
<?php else: ?>
CSVã¢ããããŒãã§äœæãããæ¡åŒµããŒã¿ããŒãã«
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<h3>管çæäœ</h3>
<form method="post" style="margin-top: 20px;" onsubmit="return confirm('å
šãŠã®åçããŒãã«ãåé€ããŸããïŒãã®æäœã¯åãæ¶ããŸããã');">
<input type="hidden" name="action" value="delete_dynamic_tables">
<button type="submit" class="btn btn-danger">
ðïž å
šãŠã®åçããŒãã«ãåé€
</button>
</form>
<p style="margin-top: 20px; color: #6c757d; font-size: 14px;">
åçããŒãã«ãåé€ãããšã次åã®CSVã¢ããããŒãæã«æ°ããæ§é ã§ããŒãã«ãåäœæãããŸãã
</p>
</div>
</body>
</html>
<?php
/**
* device_infoããŒãã«ã«created_byãupdated_byã«ã©ã ã远å ãããã€ã°ã¬ãŒã·ã§ã³
*/
require_once __DIR__ . '/../config.php';
try {
// ããŒã¿ããŒã¹æ¥ç¶
$dbType = defined('DB_TYPE') ? DB_TYPE : 'mysql';
$charset = ($dbType === 'pgsql') ? 'utf8' : DB_CHARSET;
$database = new Database(DB_HOST, DB_NAME, DB_USER, DB_PASS, $charset, $dbType, defined('DB_PORT') ? DB_PORT : null);
echo "ããŒã¿ããŒã¹ã«æ¥ç¶ããŸããã\n\n";
// æ¢åã®ã«ã©ã ã確èª
if ($dbType === 'pgsql') {
$checkSql = "
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'device_info'
AND column_name IN ('created_by', 'updated_by')
";
} else {
$checkSql = "
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'device_info'
AND TABLE_SCHEMA = '" . DB_NAME . "'
AND COLUMN_NAME IN ('created_by', 'updated_by')
";
}
$stmt = $database->execute($checkSql);
$existingColumns = $stmt->fetchAll();
$existingColumnNames = array_column($existingColumns, $dbType === 'pgsql' ? 'column_name' : 'COLUMN_NAME');
echo "æ¢åã®ã«ã©ã : " . implode(', ', $existingColumnNames) . "\n\n";
// created_byã«ã©ã ã远å
if (!in_array('created_by', $existingColumnNames)) {
echo "created_byã«ã©ã ã远å äž...\n";
if ($dbType === 'pgsql') {
$sql = "ALTER TABLE device_info ADD COLUMN created_by VARCHAR(100)";
} else {
$sql = "ALTER TABLE device_info ADD COLUMN created_by VARCHAR(100) COMMENT 'äœæè
' AFTER password10";
}
$database->execute($sql);
echo "â created_byã«ã©ã ã远å ããŸããã\n\n";
} else {
echo "created_byã«ã©ã ã¯æ¢ã«ååšããŸãã\n\n";
}
// updated_byã«ã©ã ã远å
if (!in_array('updated_by', $existingColumnNames)) {
echo "updated_byã«ã©ã ã远å äž...\n";
if ($dbType === 'pgsql') {
$sql = "ALTER TABLE device_info ADD COLUMN updated_by VARCHAR(100)";
} else {
$sql = "ALTER TABLE device_info ADD COLUMN updated_by VARCHAR(100) COMMENT 'æŽæ°è
' AFTER created_by";
}
$database->execute($sql);
echo "â updated_byã«ã©ã ã远å ããŸããã\n\n";
} else {
echo "updated_byã«ã©ã ã¯æ¢ã«ååšããŸãã\n\n";
}
echo "ãã€ã°ã¬ãŒã·ã§ã³å®äºïŒ\n";
} catch (Exception $e) {
echo "ãšã©ãŒ: " . $e->getMessage() . "\n";
exit(1);
}