PR 6: Database migration validation - fresh/upgrade test complete

 Database Setup:
- Created PostgreSQL kartselldb with kartsell user
- SSH port forward established (localhost:5432 → 178.104.200.7:5432)

 DbMigrator Fixes:
- Fixed migration path discovery (AppContext.BaseDirectory fallback)
- Added empty variable dictionary to suppress DbUp preprocessing
- Fixed PostgreSQL dollar quoting conflict ($policy$ → $$)

 Migration Results:
- All 21 migrations executed successfully
- Schema versions journal created and tracked
- 21 scripts processed in order, no rollback needed

Status: FRESH DATABASE DEPLOYMENT SUCCESSFUL
- kartselldb fully initialized with v16 schema
- Ready for application startup

Next: Deploy application and run integration tests

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 06:45:20 +09:00
parent fc39c8d4bf
commit 3b76070394
135 changed files with 6673 additions and 2 deletions
@@ -0,0 +1,67 @@
const __VLS_props = defineProps();
const __VLS_ctx = {
...{},
...{},
...{},
};
let __VLS_components;
let __VLS_intrinsics;
let __VLS_directives;
/** @type {__VLS_StyleScopedClasses['ks-shell__header']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-shell__header']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-skip']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-shell']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-shell__header']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-shell__nav']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-shell" },
});
/** @type {__VLS_StyleScopedClasses['ks-shell']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.a, __VLS_intrinsics.a)({
...{ class: "ks-skip" },
href: "#ks-main",
});
/** @type {__VLS_StyleScopedClasses['ks-skip']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.header, __VLS_intrinsics.header)({
...{ class: "ks-shell__header" },
});
/** @type {__VLS_StyleScopedClasses['ks-shell__header']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({});
(__VLS_ctx.productName ?? 'K-ArtSell Aegis');
__VLS_asFunctionalElement1(__VLS_intrinsics.small, __VLS_intrinsics.small)({});
(__VLS_ctx.environment ?? 'IMPLEMENTATION_TEMPLATE');
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-shell__boundary" },
role: "status",
});
/** @type {__VLS_StyleScopedClasses['ks-shell__boundary']} */ ;
(__VLS_ctx.automationStatus ?? '투자자문형 · 자동주문/KIS 제출 OFF · 자동 모델승격 OFF');
var __VLS_0 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.aside, __VLS_intrinsics.aside)({
...{ class: "ks-shell__nav" },
'aria-label': "주요 메뉴",
});
/** @type {__VLS_StyleScopedClasses['ks-shell__nav']} */ ;
var __VLS_2 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.main, __VLS_intrinsics.main)({
id: "ks-main",
...{ class: "ks-shell__main" },
tabindex: "-1",
});
/** @type {__VLS_StyleScopedClasses['ks-shell__main']} */ ;
var __VLS_4 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.footer, __VLS_intrinsics.footer)({
...{ class: "ks-shell__footer" },
});
/** @type {__VLS_StyleScopedClasses['ks-shell__footer']} */ ;
var __VLS_6 = {};
// @ts-ignore
var __VLS_1 = __VLS_0, __VLS_3 = __VLS_2, __VLS_5 = __VLS_4, __VLS_7 = __VLS_6;
// @ts-ignore
[productName, environment, automationStatus,];
const __VLS_base = (await import('vue')).defineComponent({
__typeProps: {},
});
const __VLS_export = {};
export default {};
@@ -0,0 +1,50 @@
const __VLS_props = withDefaults(defineProps(), { asideWidth: '24rem', dense: false }); // @ts-ignore
const __VLS_defaults = { asideWidth: '24rem', dense: false };
const __VLS_ctx = {
...{},
...{},
...{},
};
let __VLS_components;
let __VLS_intrinsics;
let __VLS_directives;
/** @type {__VLS_StyleScopedClasses['ks-crud-workspace__body']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-crud-workspace__body']} */ ;
/** @type {__VLS_StyleScopedClasses['has-aside']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-crud-workspace" },
...{ class: ({ dense: __VLS_ctx.dense }) },
...{ style: ({ '--ks-crud-aside': __VLS_ctx.asideWidth }) },
});
/** @type {__VLS_StyleScopedClasses['ks-crud-workspace']} */ ;
/** @type {__VLS_StyleScopedClasses['dense']} */ ;
if (__VLS_ctx.$slots.toolbar) {
__VLS_asFunctionalElement1(__VLS_intrinsics.header, __VLS_intrinsics.header)({});
var __VLS_0 = {};
}
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-crud-workspace__body" },
...{ class: ({ 'has-aside': __VLS_ctx.$slots.aside }) },
});
/** @type {__VLS_StyleScopedClasses['ks-crud-workspace__body']} */ ;
/** @type {__VLS_StyleScopedClasses['has-aside']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.main, __VLS_intrinsics.main)({});
var __VLS_2 = {};
if (__VLS_ctx.$slots.aside) {
__VLS_asFunctionalElement1(__VLS_intrinsics.aside, __VLS_intrinsics.aside)({});
var __VLS_4 = {};
}
if (__VLS_ctx.$slots.actions) {
__VLS_asFunctionalElement1(__VLS_intrinsics.footer, __VLS_intrinsics.footer)({});
var __VLS_6 = {};
}
// @ts-ignore
var __VLS_1 = __VLS_0, __VLS_3 = __VLS_2, __VLS_5 = __VLS_4, __VLS_7 = __VLS_6;
// @ts-ignore
[dense, asideWidth, $slots, $slots, $slots, $slots,];
const __VLS_base = (await import('vue')).defineComponent({
__typeProps: {},
props: {},
});
const __VLS_export = {};
export default {};
@@ -0,0 +1,38 @@
const __VLS_ctx = {};
let __VLS_components;
let __VLS_intrinsics;
let __VLS_directives;
/** @type {__VLS_StyleScopedClasses['ks-dashboard__alerts']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-dashboard']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-dashboard" },
});
/** @type {__VLS_StyleScopedClasses['ks-dashboard']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-dashboard__kpis" },
});
/** @type {__VLS_StyleScopedClasses['ks-dashboard__kpis']} */ ;
var __VLS_0 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-dashboard__primary ks-card" },
});
/** @type {__VLS_StyleScopedClasses['ks-dashboard__primary']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
var __VLS_2 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-dashboard__secondary ks-card" },
});
/** @type {__VLS_StyleScopedClasses['ks-dashboard__secondary']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
var __VLS_4 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-dashboard__alerts ks-card" },
});
/** @type {__VLS_StyleScopedClasses['ks-dashboard__alerts']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
var __VLS_6 = {};
// @ts-ignore
var __VLS_1 = __VLS_0, __VLS_3 = __VLS_2, __VLS_5 = __VLS_4, __VLS_7 = __VLS_6;
const __VLS_base = (await import('vue')).defineComponent({});
const __VLS_export = {};
export default {};
@@ -0,0 +1,43 @@
const __VLS_emit = defineEmits(); // @ts-ignore
const __VLS_ctx = {
...{},
...{},
...{},
...{},
};
let __VLS_components;
let __VLS_intrinsics;
let __VLS_directives;
/** @type {__VLS_StyleScopedClasses['ks-form-layout']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-form-layout" },
});
/** @type {__VLS_StyleScopedClasses['ks-form-layout']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.form, __VLS_intrinsics.form)({
...{ onSubmit: (...[$event]) => {
return (__VLS_ctx.$emit('submit'));
// @ts-ignore
[$emit,];
} },
...{ class: "ks-form-layout__form ks-card" },
});
/** @type {__VLS_StyleScopedClasses['ks-form-layout__form']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
var __VLS_0 = {};
if (__VLS_ctx.$slots.preview) {
__VLS_asFunctionalElement1(__VLS_intrinsics.aside, __VLS_intrinsics.aside)({
...{ class: "ks-form-layout__preview ks-card" },
});
/** @type {__VLS_StyleScopedClasses['ks-form-layout__preview']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
var __VLS_2 = {};
}
// @ts-ignore
var __VLS_1 = __VLS_0, __VLS_3 = __VLS_2;
// @ts-ignore
[$slots,];
const __VLS_base = (await import('vue')).defineComponent({
__typeEmits: {},
});
const __VLS_export = {};
export default {};
@@ -0,0 +1,44 @@
const __VLS_props = defineProps(); // @ts-ignore
const __VLS_ctx = {
...{},
...{},
...{},
};
let __VLS_components;
let __VLS_intrinsics;
let __VLS_directives;
/** @type {__VLS_StyleScopedClasses['ks-ops-console__grid']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-ops-console" },
});
/** @type {__VLS_StyleScopedClasses['ks-ops-console']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.header, __VLS_intrinsics.header)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.h1, __VLS_intrinsics.h1)({});
(__VLS_ctx.title);
__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({});
(__VLS_ctx.runStatus ?? '-');
(__VLS_ctx.watermark ?? '-');
(__VLS_ctx.owner ?? '-');
var __VLS_0 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-ops-console__grid" },
});
/** @type {__VLS_StyleScopedClasses['ks-ops-console__grid']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.aside, __VLS_intrinsics.aside)({});
var __VLS_2 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.main, __VLS_intrinsics.main)({});
var __VLS_4 = {};
if (__VLS_ctx.$slots.footer) {
__VLS_asFunctionalElement1(__VLS_intrinsics.footer, __VLS_intrinsics.footer)({});
var __VLS_6 = {};
}
// @ts-ignore
var __VLS_1 = __VLS_0, __VLS_3 = __VLS_2, __VLS_5 = __VLS_4, __VLS_7 = __VLS_6;
// @ts-ignore
[title, runStatus, watermark, owner, $slots,];
const __VLS_base = (await import('vue')).defineComponent({
__typeProps: {},
});
const __VLS_export = {};
export default {};
@@ -0,0 +1,98 @@
const __VLS_props = defineProps();
const __VLS_ctx = {
...{},
...{},
...{},
};
let __VLS_components;
let __VLS_intrinsics;
let __VLS_directives;
/** @type {__VLS_StyleScopedClasses['ks-page__workspace']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-page__workspace']} */ ;
/** @type {__VLS_StyleScopedClasses['has-aside']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-page" },
...{ style: ({ '--ks-aside-width': __VLS_ctx.asideWidth ?? '22rem' }) },
});
/** @type {__VLS_StyleScopedClasses['ks-page']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.header, __VLS_intrinsics.header)({
...{ class: "ks-page__header" },
});
/** @type {__VLS_StyleScopedClasses['ks-page__header']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.h1, __VLS_intrinsics.h1)({});
(__VLS_ctx.title);
if (__VLS_ctx.subtitle) {
__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({});
(__VLS_ctx.subtitle);
}
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-page__meta" },
});
/** @type {__VLS_StyleScopedClasses['ks-page__meta']} */ ;
if (__VLS_ctx.status) {
__VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({});
(__VLS_ctx.status);
}
if (__VLS_ctx.asOf) {
__VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({});
(__VLS_ctx.asOf);
}
if (__VLS_ctx.version) {
__VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({});
(__VLS_ctx.version);
}
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-page__actions" },
});
/** @type {__VLS_StyleScopedClasses['ks-page__actions']} */ ;
var __VLS_0 = {};
if (__VLS_ctx.$slots.summary) {
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-page__summary" },
});
/** @type {__VLS_StyleScopedClasses['ks-page__summary']} */ ;
var __VLS_2 = {};
}
if (__VLS_ctx.$slots.filters) {
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-page__filters ks-card" },
});
/** @type {__VLS_StyleScopedClasses['ks-page__filters']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
var __VLS_4 = {};
}
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-page__workspace" },
...{ class: ({ 'has-aside': __VLS_ctx.$slots.aside }) },
});
/** @type {__VLS_StyleScopedClasses['ks-page__workspace']} */ ;
/** @type {__VLS_StyleScopedClasses['has-aside']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-page__content" },
});
/** @type {__VLS_StyleScopedClasses['ks-page__content']} */ ;
var __VLS_6 = {};
if (__VLS_ctx.$slots.aside) {
__VLS_asFunctionalElement1(__VLS_intrinsics.aside, __VLS_intrinsics.aside)({
...{ class: "ks-page__aside" },
});
/** @type {__VLS_StyleScopedClasses['ks-page__aside']} */ ;
var __VLS_8 = {};
}
if (__VLS_ctx.$slots.footer) {
__VLS_asFunctionalElement1(__VLS_intrinsics.footer, __VLS_intrinsics.footer)({
...{ class: "ks-page__footer" },
});
/** @type {__VLS_StyleScopedClasses['ks-page__footer']} */ ;
var __VLS_10 = {};
}
// @ts-ignore
var __VLS_1 = __VLS_0, __VLS_3 = __VLS_2, __VLS_5 = __VLS_4, __VLS_7 = __VLS_6, __VLS_9 = __VLS_8, __VLS_11 = __VLS_10;
// @ts-ignore
[asideWidth, title, subtitle, subtitle, status, status, asOf, asOf, version, version, $slots, $slots, $slots, $slots, $slots,];
const __VLS_base = (await import('vue')).defineComponent({
__typeProps: {},
});
const __VLS_export = {};
export default {};
@@ -0,0 +1,36 @@
const __VLS_ctx = {};
let __VLS_components;
let __VLS_intrinsics;
let __VLS_directives;
/** @type {__VLS_StyleScopedClasses['ks-review-workbench']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-review-workbench']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-review-workbench" },
});
/** @type {__VLS_StyleScopedClasses['ks-review-workbench']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-review-workbench__queue ks-card" },
'aria-label': "검토 대기열",
});
/** @type {__VLS_StyleScopedClasses['ks-review-workbench__queue']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
var __VLS_0 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-review-workbench__detail ks-card" },
'aria-label': "결정 상세",
});
/** @type {__VLS_StyleScopedClasses['ks-review-workbench__detail']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
var __VLS_2 = {};
__VLS_asFunctionalElement1(__VLS_intrinsics.aside, __VLS_intrinsics.aside)({
...{ class: "ks-review-workbench__decision ks-card" },
'aria-label': "검토 결정",
});
/** @type {__VLS_StyleScopedClasses['ks-review-workbench__decision']} */ ;
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
var __VLS_4 = {};
// @ts-ignore
var __VLS_1 = __VLS_0, __VLS_3 = __VLS_2, __VLS_5 = __VLS_4;
const __VLS_base = (await import('vue')).defineComponent({});
const __VLS_export = {};
export default {};
+7
View File
@@ -0,0 +1,7 @@
export { default as AppShellLayout } from './AppShellLayout.vue';
export { default as PageLayout } from './PageLayout.vue';
export { default as DashboardLayout } from './DashboardLayout.vue';
export { default as FormPageLayout } from './FormPageLayout.vue';
export { default as ReviewWorkbenchLayout } from './ReviewWorkbenchLayout.vue';
export { default as CrudWorkspaceLayout } from './CrudWorkspaceLayout.vue';
export { default as OperationsConsoleLayout } from './OperationsConsoleLayout.vue';