fix #495: add schema preflight and DDL details

This commit is contained in:
devmrko
2026-06-26 16:32:38 +09:00
parent cacf7b2bc4
commit 5d864379bd
8 changed files with 922 additions and 182 deletions

View File

@@ -1098,6 +1098,85 @@ body {
white-space: pre-wrap;
}
.schema-summary {
display: grid;
gap: .75rem;
grid-template-columns: repeat(6, minmax(0, 1fr));
}
.schema-summary div {
background: var(--rw-surface-muted);
border: 1px solid var(--rw-border);
border-radius: 8px;
min-width: 0;
padding: .7rem .8rem;
}
.schema-summary span {
color: var(--rw-muted);
display: block;
font-size: .76rem;
line-height: 1.35;
}
.schema-summary strong {
color: var(--rw-text);
display: block;
font-size: .98rem;
font-weight: 800;
line-height: 1.35;
overflow-wrap: anywhere;
}
.schema-preview-grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.schema-preview-grid details {
border: 1px solid var(--rw-border);
border-radius: 8px;
min-width: 0;
}
.schema-preview-grid summary {
cursor: pointer;
font-weight: 800;
padding: .75rem .9rem;
}
.schema-sql-preview {
background: #211f1d;
border-top: 1px solid var(--rw-border);
color: #fbf9f7;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
font-size: .76rem;
line-height: 1.45;
margin: 0;
max-height: 28rem;
overflow: auto;
padding: .85rem;
white-space: pre-wrap;
word-break: break-word;
}
.table-responsive > .schema-preflight-table th,
.table-responsive > .schema-preflight-table td,
.table-responsive > .schema-preflight-table code {
overflow-wrap: anywhere;
white-space: normal;
}
.schema-preflight-table th:nth-child(1),
.schema-preflight-table td:nth-child(1),
.schema-preflight-table th:nth-child(3),
.schema-preflight-table td:nth-child(3),
.schema-preflight-table th:nth-child(4),
.schema-preflight-table td:nth-child(4) {
width: 8rem;
}
.policy-function-button {
border-radius: 8px;
max-width: 100%;
@@ -1329,6 +1408,14 @@ body {
}
@media (max-width: 920px) {
.schema-summary {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.schema-preview-grid {
grid-template-columns: 1fr;
}
.architecture-strip {
grid-template-columns: 1fr;
}
@@ -1339,6 +1426,10 @@ body {
}
@media (max-width: 640px) {
.schema-summary {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
main.container {
max-width: 100%;
min-width: 0;

View File

@@ -10,10 +10,15 @@
</div>
<div class="alert alert-success" th:if="${message}" th:text="${message}"></div>
<div class="alert alert-danger" th:if="${error}" th:text="${error}"></div>
<div class="alert alert-warning" th:if="${runtimeErrorMessage}">
<strong th:text="${runtimeErrorTitle}">DB 준비 필요</strong>
<div th:text="${runtimeErrorMessage}">지원 테이블을 생성하세요.</div>
</div>
<div class="alert alert-warning" th:if="${schemaPreflightErrorMessage}">
<strong th:text="${schemaPreflightErrorTitle}">DB 준비 상태 확인 불가</strong>
<div th:text="${schemaPreflightErrorMessage}">DB 연결을 확인하세요.</div>
</div>
<section class="content-band">
<h2>ORDS</h2>
@@ -27,10 +32,97 @@
</form>
</section>
<section class="content-band mt-3" th:if="${schemaPreflight}">
<div class="section-heading">
<div>
<h2>DB 준비 상태</h2>
<p>현재 연결 사용자 기준으로 백오피스 메타데이터, VPD runtime, ORDS runtime, grant 상태를 점검합니다.</p>
</div>
</div>
<div class="schema-summary">
<div>
<span>현재 연결 사용자</span>
<strong th:text="${schemaPreflight.currentUser()}">ADMIN</strong>
</div>
<div>
<span>권장 ORDS parsing schema</span>
<strong th:text="${schemaPreflight.recommendedOrdsSchema()}">CB_ORDS</strong>
</div>
<div>
<span>OK</span>
<strong th:text="${schemaPreflight.okCount()}">0</strong>
</div>
<div>
<span>WARN</span>
<strong th:text="${schemaPreflight.warnCount()}">0</strong>
</div>
<div>
<span>MISSING</span>
<strong th:text="${schemaPreflight.missingCount()}">0</strong>
</div>
<div>
<span>ERROR</span>
<strong th:text="${schemaPreflight.errorCount()}">0</strong>
</div>
</div>
<div class="alert alert-info mt-3 mb-3" th:if="${schemaPreflight.hasActionNeeded()}">
앱에서 실행 가능한 항목은 아래 `지원 테이블 생성/확인` 버튼으로 보강하고, `sqlcl 필요` 항목은 preview 순서대로 DB 권한을 가진 세션에서 실행하세요.
</div>
<div class="table-responsive">
<table class="table table-sm align-middle schema-preflight-table">
<thead>
<tr>
<th>Layer</th>
<th>Item</th>
<th>Type</th>
<th>Status</th>
<th>Detail</th>
<th>Next Action</th>
</tr>
</thead>
<tbody>
<tr th:each="check : ${schemaPreflight.checks()}">
<td th:text="${check.layer()}">Backoffice Metadata</td>
<td><code th:text="${check.itemName()}">CB_APP_USER</code></td>
<td th:text="${check.objectType()}">TABLE</td>
<td>
<span class="badge"
th:classappend="${check.ok()} ? ' text-bg-success' : (${check.warning()} ? ' text-bg-warning' : (${check.missing()} ? ' text-bg-secondary' : ' text-bg-danger'))"
th:text="${check.status()}">OK</span>
</td>
<td th:text="${check.detail()}">존재합니다.</td>
<td>
<span class="badge" th:classappend="${check.sqlclRequired()} ? ' text-bg-warning' : ' text-bg-info'"
th:text="${check.sqlclRequired()} ? 'sqlcl 필요' : '앱 실행 가능'">앱 실행 가능</span>
<div class="mt-1" th:text="${check.nextAction()}">조치 없음</div>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="content-band mt-3" th:if="${schemaPreflight}">
<h2>SQL Preview</h2>
<p class="text-muted mb-3">
앱 버튼이 실행할 SQL과 sqlcl로 실행할 runtime 준비 순서를 구분해서 확인합니다.
</p>
<div class="schema-preview-grid">
<details open>
<summary>앱에서 실행하는 지원 DDL</summary>
<pre class="schema-sql-preview" th:text="${schemaPreflight.appDdlPreview()}">CREATE TABLE ...</pre>
</details>
<details>
<summary>sqlcl로 실행할 VPD/ORDS runtime 순서</summary>
<pre class="schema-sql-preview" th:text="${schemaPreflight.sqlclScript()}">@sql/adb/...</pre>
</details>
</div>
</section>
<section class="content-band mt-3">
<h2>DB 스키마 초기화</h2>
<p class="text-muted mb-3">
현재 연결된 DB에 백오피스가 사용하는 지원 테이블 시퀀스 생성하거나 보강합니다.
현재 연결된 DB 사용자에 백오피스 지원 테이블, 시퀀스, 컬럼을 생성하거나 보강합니다. VPD context, ORDS enablement, grant는 sqlcl preview 순서를 사용하세요.
</p>
<form method="post" action="/settings/schema/initialize" class="inline-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
@@ -44,6 +136,7 @@
<th>Action</th>
<th>Status</th>
<th>Message</th>
<th>SQL</th>
</tr>
</thead>
<tbody>
@@ -52,10 +145,17 @@
<td th:text="${result.action()}">TABLE</td>
<td>
<span class="badge"
th:classappend="${result.status() == 'CREATED' || result.status() == 'MERGED'} ? ' text-bg-success' : (${result.status() == 'EXISTS'} ? ' text-bg-secondary' : ' text-bg-danger')"
th:classappend="${result.status() == 'CREATED' || result.status() == 'MERGED' || result.status() == 'UPDATED'} ? ' text-bg-success' : (${result.status() == 'EXISTS'} ? ' text-bg-secondary' : ' text-bg-danger')"
th:text="${result.status()}">CREATED</span>
</td>
<td th:text="${result.message()}">생성했습니다.</td>
<td>
<details th:if="${result.sqlText()}">
<summary>보기</summary>
<pre class="table-pre" th:text="${result.sqlText()}">CREATE TABLE ...</pre>
</details>
<span class="text-muted" th:unless="${result.sqlText()}">-</span>
</td>
</tr>
</tbody>
</table>