POSA_LEAKSMS/docs/examples/monthly_report_2026-05_dummy.html
유창욱 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

135 lines
6.2 KiB
HTML

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>월간 보고서 - 2026-05</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; font-family: 'Noto Sans KR', sans-serif; background: #f0f4f8; color: #1f2937; }
header { background: #1a56db; color: #fff; padding: 16px 24px; display:flex; justify-content:space-between; align-items:center; gap:12px; }
header h1 { margin:0; font-size:1.15rem; }
header a { color:#fff; text-decoration:none; background:rgba(255,255,255,.15); padding:7px 12px; border-radius:6px; font-size:.85rem; }
.container { max-width: 1060px; margin: 24px auto; padding: 0 16px; }
.toolbar { background:#fff; border-radius:8px; padding:14px 16px; box-shadow:0 1px 4px rgba(0,0,0,.1); display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin-bottom:18px; }
input, button, .btn { border:1px solid #cbd5e1; border-radius:6px; padding:8px 10px; font-size:.9rem; }
button, .btn { background:#1a56db; color:#fff; border-color:#1a56db; text-decoration:none; cursor:pointer; }
.toolbar label { display:flex; align-items:center; gap:6px; }
.grid { display:grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap:12px; margin-bottom:18px; }
.card { background:#fff; border-radius:8px; padding:16px; box-shadow:0 1px 4px rgba(0,0,0,.1); }
.card span { color:#64748b; font-size:.78rem; display:block; margin-bottom:5px; }
.card strong { font-size:1.35rem; }
table { width:100%; border-collapse:collapse; background:#fff; border-radius:8px; overflow:hidden; box-shadow:0 1px 4px rgba(0,0,0,.1); margin-bottom:22px; }
th, td { padding:9px 12px; border-bottom:1px solid #e2e8f0; text-align:left; font-size:.84rem; }
th { background:#f8fafc; color:#64748b; }
h2 { font-size:1rem; color:#475569; margin:22px 0 10px; }
.status { font-weight:700; }
.table-wrap { overflow-x:auto; -webkit-overflow-scrolling:touch; margin-bottom:22px; border-radius:8px; }
.table-wrap table { margin-bottom:0; min-width:720px; }
@media print {
body { background:#fff; }
header, .toolbar { display:none; }
.container { margin:0; max-width:none; }
.card, table { box-shadow:none; }
}
@media (max-width:760px) { .grid { grid-template-columns: repeat(2, minmax(0,1fr)); } header { flex-direction:column; align-items:flex-start; } }
@media (max-width:520px) {
.container { margin:16px auto; padding:0 10px; }
header { padding:14px 16px; }
header h1 { font-size:1rem; }
.toolbar { display:grid; grid-template-columns:1fr; }
.toolbar label, .toolbar input, .toolbar button, .toolbar .btn { width:100%; }
.toolbar label { display:grid; gap:6px; }
.grid { grid-template-columns: repeat(2, minmax(0,1fr)); gap:8px; }
.card { padding:12px; }
.card strong { font-size:1.05rem; }
}
@media (max-width:360px) { .grid { grid-template-columns:1fr; } }
</style>
</head>
<body>
<header>
<h1>2026-05 월간 운영 보고서</h1>
<div>
<a href="#">대시보드</a>
</div>
</header>
<main class="container">
<form class="toolbar" method="GET">
<label><input type="month" name="month" value="2026-05"></label>
<button type="submit">조회</button>
<a class="btn" href="#">CSV 다운로드</a>
<button type="button" onclick="window.print()">인쇄/PDF</button>
</form>
<section class="card" style="margin-bottom:18px">
<span>보고서 생성</span>
<strong style="font-size:1rem">2026-05-20 13:50:00</strong>
<p style="color:#64748b;font-size:.84rem;margin:8px 0 0">범위: 온습도 임계 경보, 장비 오프라인, SMS, 정상복귀/복구</p>
</section>
<section class="grid">
<div class="card"><span>고온 경보</span><strong>2건</strong></div>
<div class="card"><span>저온 경보</span><strong>0건</strong></div>
<div class="card"><span>고습 경보</span><strong>1건</strong></div>
<div class="card"><span>저습 경보</span><strong>0건</strong></div>
<div class="card"><span>정상복귀</span><strong>3건</strong></div>
<div class="card"><span>장비 오프라인</span><strong>1건</strong></div>
<div class="card"><span>SMS 성공/실패</span><strong>8/1</strong></div>
<div class="card"><span>측정 건수</span><strong>8,640건</strong></div>
</section>
<h2>센서별 요약</h2>
<div class="table-wrap">
<table>
<thead><tr><th>센서</th><th>위치</th><th>임계 경보</th><th>평균 온도</th><th>평균 습도</th><th>최근 측정</th></tr></thead>
<tbody>
<tr>
<td>센서 1</td>
<td>전산실 서버랙 A열 하단</td>
<td>2</td>
<td>24.1C</td>
<td>47.3%</td>
<td>2026-05-18 09:41:20</td>
</tr>
<tr>
<td>센서 2</td>
<td>전산실 상단 랙</td>
<td>1</td>
<td>25.4C</td>
<td>52.0%</td>
<td>2026-05-31 23:59:11</td>
</tr>
</tbody>
</table>
</div>
<h2>임계 경보 SMS 내역</h2>
<div class="table-wrap">
<table>
<thead><tr><th>발생</th><th>센서</th><th>위치</th><th>경보 종류</th><th>측정값</th><th>정상복귀</th><th>SMS</th></tr></thead>
<tbody>
<tr>
<td>2026-05-12 16:08:11</td>
<td>센서 2</td>
<td>전산실 상단 랙</td>
<td class="status">고습</td>
<td>25.1C / 72.4%</td>
<td>2026-05-12 16:38:30</td>
<td>성공</td>
</tr>
<tr>
<td>2026-05-18 09:13:42</td>
<td>센서 1</td>
<td>전산실 서버랙 A열 하단</td>
<td class="status">고온</td>
<td>31.2C / 41.0%</td>
<td>2026-05-18 09:45:20</td>
<td>성공</td>
</tr>
</tbody>
</table>
</div>
</main>
</body>
</html>