POSA_LEAKSMS/sql/migration_drop_leak.sql
유창욱 90f121e14c chore: import codebase with security hardening
SHT30 온습도 모니터링 시스템 전체 소스(서버 PHP, STM32 펌웨어, SQL, 테스트).
전체 코드리뷰에서 도출된 보안 하드닝 10건 반영:
- 요청 서명 HMAC-SHA256 전환(펌웨어 sig.c/서버 config.php/호스트 패리티 동시)
- 재전송 방어 + 기본 API_KEY fail-closed + 디바이스 문자열 정제(api/sensor_data.php)
- 오프라인 SMS 중복 발송 경합 제거(cron_heartbeat.php, 원자적 선점)
- CSV 수식 주입 방지(monthly_report.php), 감사로그 회전 락(retention_cleanup.php)
- 브루트포스 카운터 원자화(login.php), 예시 TOTP 비밀키 무효화, 마이그레이션 멱등화

_backup/(하드코딩 실 비밀값 포함)·config.local.php·런타임 상태는 .gitignore 제외.
2026-06-20 09:37:40 +09:00

95 lines
5 KiB
SQL

-- =============================================================================
-- migration_drop_leak.sql - 기존 누수(v2605) 설치 → SHT30 온습도 전용 전환
--
-- ⚠️ 비가역(DROP COLUMN/TABLE) 변경입니다. 반드시 DB 전체 백업 후 실행하세요.
-- 운영 데이터(과거 누수 이벤트/사진/사고 이력)가 삭제됩니다.
--
-- 이미 v2605 스키마(sensor_log/sensor_status/sensor_metric/sms_log + leak_*)가
-- 적용된 운영 DB를 SHT30 전용으로 정리합니다. 측정 이력(sensor_metric)과 SMS
-- 이력(sms_log)은 보존합니다.
--
-- 신규 설치라면 이 파일이 아니라 sql/schema_sht30.sql 하나만 실행하세요.
-- =============================================================================
SET NAMES utf8mb4;
-- 0) sensor_metric 이 없던 구버전 대비: 없으면 생성(있으면 무시).
CREATE TABLE IF NOT EXISTS `sensor_metric` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`device_id` VARCHAR(64) NOT NULL DEFAULT '',
`device_location` VARCHAR(100) NOT NULL DEFAULT '',
`sensor_id` TINYINT UNSIGNED NOT NULL,
`sensor_name` VARCHAR(100) NOT NULL DEFAULT '',
`metric_type` VARCHAR(30) NOT NULL DEFAULT 'sht30',
`temperature_c` DECIMAL(6,2) NULL,
`humidity_percent` DECIMAL(5,2) NULL,
`metric_status` VARCHAR(30) NOT NULL DEFAULT 'normal',
`measured_at` DATETIME NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
INDEX `idx_sensor_measured` (`sensor_id`, `measured_at`),
INDEX `idx_device_sensor` (`device_id`, `sensor_id`),
INDEX `idx_metric_type` (`metric_type`, `created_at`),
INDEX `idx_status_measured` (`metric_status`, `measured_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 1) 누수 전용 테이블 제거
DROP TABLE IF EXISTS `leak_photo`;
DROP TABLE IF EXISTS `leak_incident`;
-- 2) sensor_log: 누수 enum 값 정리 → event_type 재정의 (둘 다 멱등)
-- (leak_alert/leak_recovery 행이 있으면 periodic 으로 강등 후 enum 축소.
-- 이미 정리된 DB면 매칭 행이 없어 no-op, MODIFY 도 동일 enum 재적용이라 안전.)
UPDATE `sensor_log` SET `event_type` = 'periodic'
WHERE `event_type` IN ('leak_alert', 'leak_recovery');
ALTER TABLE `sensor_log`
MODIFY `event_type` ENUM('startup','periodic') NOT NULL DEFAULT 'periodic';
-- 3~4) 인덱스/컬럼 제거·추가는 "존재할 때만" 수행하여 재실행해도 오류가 없게 한다.
-- (MySQL/MariaDB 는 DROP COLUMN/INDEX 에 IF EXISTS 호환이 일정치 않으므로
-- information_schema 로 가드하는 멱등 프로시저를 사용한다.)
DROP PROCEDURE IF EXISTS _mig_drop_leak;
DELIMITER //
CREATE PROCEDURE _mig_drop_leak()
BEGIN
DECLARE db VARCHAR(64);
SELECT DATABASE() INTO db;
-- sensor_log: is_leak 인덱스/컬럼 제거, event_type 인덱스 추가
IF EXISTS (SELECT 1 FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = db AND TABLE_NAME = 'sensor_log' AND INDEX_NAME = 'idx_is_leak') THEN
ALTER TABLE `sensor_log` DROP INDEX `idx_is_leak`;
END IF;
IF EXISTS (SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = db AND TABLE_NAME = 'sensor_log' AND COLUMN_NAME = 'is_leak') THEN
ALTER TABLE `sensor_log` DROP COLUMN `is_leak`;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = db AND TABLE_NAME = 'sensor_log' AND INDEX_NAME = 'idx_event_type') THEN
ALTER TABLE `sensor_log` ADD INDEX `idx_event_type` (`event_type`, `created_at`);
END IF;
-- sensor_status: 누수 컬럼/인덱스 제거 (offline_alerted 는 오프라인 SMS에 필요 → 유지)
IF EXISTS (SELECT 1 FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = db AND TABLE_NAME = 'sensor_status' AND INDEX_NAME = 'idx_is_leak') THEN
ALTER TABLE `sensor_status` DROP INDEX `idx_is_leak`;
END IF;
IF EXISTS (SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = db AND TABLE_NAME = 'sensor_status' AND COLUMN_NAME = 'is_leak') THEN
ALTER TABLE `sensor_status` DROP COLUMN `is_leak`;
END IF;
IF EXISTS (SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = db AND TABLE_NAME = 'sensor_status' AND COLUMN_NAME = 'leak_periodic_count') THEN
ALTER TABLE `sensor_status` DROP COLUMN `leak_periodic_count`;
END IF;
-- sensor_metric: 임계 경보 조회용 인덱스 추가(없을 때만)
IF NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = db AND TABLE_NAME = 'sensor_metric' AND INDEX_NAME = 'idx_status_measured') THEN
ALTER TABLE `sensor_metric` ADD INDEX `idx_status_measured` (`metric_status`, `measured_at`);
END IF;
END //
DELIMITER ;
CALL _mig_drop_leak();
DROP PROCEDURE IF EXISTS _mig_drop_leak;