From 70824c2afbf737ce5fb1ca21996cb293aba0e280 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Thu, 30 Jul 2026 11:20:02 +0900 Subject: [PATCH] fix: security, data-integrity, and doc-drift findings from repo audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consolidates duplicate KIS API client implementations (governance tests were exercising an unused class instead of the one actually running in production), closes a SQL injection path in the DB admin page, fixes a migration that used MySQL-only syntax and had never actually applied (confirmed against production), resyncs docs/db/quantengine.dbml with all migrations, and removes a duplicate OMS·WMS·ERP frontend tree in favor of src/frontend/. Also corrects several unverifiable/inflated claims in the OMS planning docs and realigns CI/CD and architecture documentation with what's actually in the repo. Co-Authored-By: Claude Sonnet 5 --- .gitignore | 6 + CLAUDE.md | 1784 +----- docs/API_REFERENCE.md | 53 + docs/CICD_PIPELINE.md | 107 + docs/DEPLOYMENT_RUNBOOK.md | 337 ++ docs/DEV_WORKFLOWS.md | 137 + docs/MIGRATION_STATUS.md | 101 + docs/OMS_WMS_ERP_PLAYBOOK.md | 725 +++ docs/UI_GUIDELINES.md | 54 + .../CICD_ANALYSIS_AND_ROADMAP.md | 4 + docs/{ => archive}/CICD_MONITORING_GUIDE.md | 4 + .../CI_CD_IMPLEMENTATION_SUMMARY.md | 4 + .../DEPLOYMENT_TROUBLESHOOTING.md | 5 + docs/{ => archive}/TESTING_AND_DEPLOYMENT.md | 5 + docs/db/quantengine.dbml | 251 +- oms-wms-erp/.dockerignore | 12 - oms-wms-erp/.env.example | 23 - oms-wms-erp/.eslintrc.cjs | 45 - oms-wms-erp/.gitea/workflows/deploy-prod.yml | 174 - .../.gitea/workflows/prepare-release.yml | 88 - oms-wms-erp/.github/workflows/ci.yml | 146 - .../.github/workflows/deploy-storybook.yml | 48 - oms-wms-erp/.gitignore | 38 - oms-wms-erp/.husky/pre-commit | 4 - oms-wms-erp/.lintstagedrc.json | 4 - oms-wms-erp/.prettierrc.json | 12 - oms-wms-erp/.storybook/main.ts | 43 - oms-wms-erp/.storybook/preview.ts | 61 - oms-wms-erp/DEPLOYMENT.md | 106 - oms-wms-erp/DEVELOPMENT.md | 613 -- oms-wms-erp/Dockerfile | 27 - oms-wms-erp/GITHUB-SETUP.md | 481 -- oms-wms-erp/Makefile | 161 - oms-wms-erp/PHASE1-COMPLETION.md | 434 -- oms-wms-erp/PHASE1-STEP2-VERIFICATION.md | 390 -- oms-wms-erp/PHASE2-ROADMAP.md | 443 -- oms-wms-erp/PHASE2-STEP3-COMPLETION.md | 405 -- .../PHASE2-STEP4-FINAL-VERIFICATION.md | 471 -- oms-wms-erp/PHASE2-TYPED-FIELDS.md | 517 -- oms-wms-erp/PHASE3-STEP1-COMPLETION.md | 433 -- .../PHASE3-STEP1-TYPED-FIELDS-COMPLETE.md | 691 --- oms-wms-erp/PHASE3-STEP2-SMART-COMPONENTS.md | 694 --- oms-wms-erp/PRIMITIVES-IMPLEMENTATION.md | 462 -- oms-wms-erp/README.md | 206 - oms-wms-erp/TEST-GUIDE.md | 205 - oms-wms-erp/index.html | 13 - oms-wms-erp/package-lock.json | 4984 ----------------- oms-wms-erp/package.json | 48 - oms-wms-erp/playwright.config.ts | 40 - oms-wms-erp/scripts/generate-primitive.mjs | 95 - oms-wms-erp/scripts/generate-store.mjs | 164 - oms-wms-erp/src/App.vue | 163 - .../src/components/composites${component}.vue | 18 - .../composites/InventoryTransfer.vue | 75 - .../src/components/composites/OrderForm.vue | 244 - .../src/components/composites/index.ts | 29 - .../domain/AddressField/AddressField.spec.ts | 272 - .../AddressField/AddressField.stories.ts | 107 - .../domain/AddressField/AddressField.vue | 388 -- .../ApprovalField/ApprovalField.spec.ts | 341 -- .../ApprovalField/ApprovalField.stories.ts | 94 - .../domain/ApprovalField/ApprovalField.vue | 428 -- .../BankAccountField/BankAccountField.spec.ts | 318 -- .../BankAccountField.stories.ts | 62 - .../BankAccountField/BankAccountField.vue | 348 -- .../domain/CustomerField/CustomerField.vue | 416 -- .../DateRangeField/DateRangeField.spec.ts | 182 - .../DateRangeField/DateRangeField.stories.ts | 95 - .../domain/DateRangeField/DateRangeField.vue | 336 -- .../OrderLineField/OrderLineField.spec.ts | 313 -- .../OrderLineField/OrderLineField.stories.ts | 118 - .../domain/OrderLineField/OrderLineField.vue | 453 -- .../domain/ProductField/ProductField.spec.ts | 417 -- .../ProductField/ProductField.stories.ts | 152 - .../domain/ProductField/ProductField.vue | 435 -- .../fields/domain/RoleField/RoleField.spec.ts | 313 -- .../domain/RoleField/RoleField.stories.ts | 77 - .../fields/domain/RoleField/RoleField.vue | 403 -- .../StockTransferField.spec.ts | 379 -- .../StockTransferField.stories.ts | 105 - .../StockTransferField/StockTransferField.vue | 458 -- .../SupplierField/SupplierField.spec.ts | 378 -- .../SupplierField/SupplierField.stories.ts | 147 - .../domain/SupplierField/SupplierField.vue | 537 -- .../domain/TaxIDField/TaxIDField.spec.ts | 325 -- .../domain/TaxIDField/TaxIDField.stories.ts | 84 - .../fields/domain/TaxIDField/TaxIDField.vue | 395 -- .../WarehouseField/WarehouseField.spec.ts | 387 -- .../WarehouseField/WarehouseField.stories.ts | 131 - .../domain/WarehouseField/WarehouseField.vue | 618 -- .../src/components/fields/domain/index.ts | 52 - .../typed/CheckboxField/CheckboxField.vue | 96 - .../typed/CurrencyField/CurrencyField.vue | 96 - .../fields/typed/DateField/DateField.vue | 70 - .../fields/typed/EmailField/EmailField.vue | 108 - .../typed/NumberField/NumberField.spec.ts | 170 - .../typed/NumberField/NumberField.stories.ts | 91 - .../fields/typed/NumberField/NumberField.vue | 134 - .../typed/PercentageField/PercentageField.vue | 148 - .../fields/typed/PhoneField/PhoneField.vue | 114 - .../fields/typed/SelectField/SelectField.vue | 76 - .../fields/typed/StatusField/StatusField.vue | 100 - .../fields/typed/TextField/TextField.vue | 83 - .../typed/TextareaField/TextareaField.vue | 140 - .../fields/typed/URLField/URLField.vue | 111 - .../src/components/fields/typed/index.ts | 31 - .../components/primitives/AccessibleTable.vue | 208 - .../src/components/primitives/Alert.vue | 141 - .../primitives/Button/ButtonBase.spec.ts | 74 - .../primitives/Button/ButtonBase.stories.ts | 91 - .../primitives/Button/ButtonBase.vue | 63 - .../src/components/primitives/FormField.vue | 218 - .../components/primitives/Input/InputBase.vue | 49 - .../primitives/Select/SelectBase.vue | 53 - .../src/components/primitives/Spinner.vue | 66 - .../components/primitives/Table/TableBase.vue | 69 - oms-wms-erp/src/composables/useFormatting.ts | 113 - oms-wms-erp/src/composables/useValidation.ts | 111 - oms-wms-erp/src/main.ts | 13 - oms-wms-erp/src/router.ts | 122 - oms-wms-erp/src/router/admin-routes.ts | 35 - oms-wms-erp/src/router/guards.ts | 24 - oms-wms-erp/src/router/index.ts | 35 - oms-wms-erp/src/services/api/client.ts | 181 - oms-wms-erp/src/services/mockApi.ts | 113 - oms-wms-erp/src/stores/index.ts | 29 - oms-wms-erp/src/stores/modules/auth.ts | 66 - oms-wms-erp/src/stores/modules/customers.ts | 128 - oms-wms-erp/src/stores/modules/inventory.ts | 107 - oms-wms-erp/src/stores/modules/orders.ts | 201 - oms-wms-erp/src/stores/modules/pricing.ts | 100 - oms-wms-erp/src/stores/modules/products.ts | 130 - oms-wms-erp/src/stores/modules/reports.ts | 126 - oms-wms-erp/src/stores/modules/settings.ts | 106 - oms-wms-erp/src/stores/modules/suppliers.ts | 115 - oms-wms-erp/src/stores/modules/users.ts | 108 - oms-wms-erp/src/stores/modules/warehouses.ts | 105 - oms-wms-erp/src/style.css | 68 - oms-wms-erp/src/types/domains.ts | 235 - oms-wms-erp/src/types/schemas.ts | 78 - oms-wms-erp/src/utils/errors.ts | 41 - oms-wms-erp/src/utils/fieldTypes.ts | 158 - .../src/views/Customer/CustomerForm.vue | 217 - .../src/views/Customer/CustomerList.vue | 110 - oms-wms-erp/src/views/Home.vue | 98 - .../src/views/Inventory/InventoryList.vue | 46 - oms-wms-erp/src/views/LoginPage.vue | 145 - oms-wms-erp/src/views/NotFound.vue | 25 - oms-wms-erp/src/views/Order/OrderList.vue | 52 - oms-wms-erp/src/views/Product/ProductList.vue | 51 - .../src/views/Warehouse/WarehouseForm.vue | 179 - .../src/views/Warehouse/WarehouseList.vue | 51 - oms-wms-erp/src/views/admin${form}.vue | 38 - oms-wms-erp/src/views/admin${page}.vue | 43 - oms-wms-erp/src/views/admin/Dashboard.vue | 253 - oms-wms-erp/src/views/admin/OrderForm.vue | 197 - oms-wms-erp/src/views/admin/ProductForm.vue | 212 - oms-wms-erp/src/views/admin/index.ts | 6 - oms-wms-erp/tests/e2e/authentication.spec.ts | 112 - oms-wms-erp/tests/e2e/complete-flow.spec.ts | 258 - oms-wms-erp/tests/e2e/form-submission.spec.ts | 136 - oms-wms-erp/tests/e2e/full-navigation.spec.ts | 29 - oms-wms-erp/tests/e2e/index.ts | 12 - oms-wms-erp/tests/e2e/inventory-flow.spec.ts | 49 - oms-wms-erp/tests/e2e/order-flow.spec.ts | 122 - .../integration/customer-workflow.spec.ts | 111 - oms-wms-erp/tests/integration/index.ts | 14 - .../integration/inventory-workflow.spec.ts | 111 - .../tests/integration/inventory.spec.ts | 201 - .../tests/integration/order-workflow.spec.ts | 221 - oms-wms-erp/tests/integration/orders.spec.ts | 221 - .../tests/integration/products.spec.ts | 297 - oms-wms-erp/tests/mocks/handlers.ts | 193 - oms-wms-erp/tests/mocks/server.ts | 22 - oms-wms-erp/tests/setup.ts | 42 - oms-wms-erp/tsconfig.json | 33 - oms-wms-erp/vite.config.ts | 45 - oms-wms-erp/vitest.config.ts | 22 - spec/61_strategic_execution_framework.yaml | 7 +- .../QuantEngine.Core.Tests/SecurityTests.cs | 54 +- .../External/KisApiClient.cs | 250 - .../V003_add_audit_trail_tables.sql | 35 +- .../Services/KisApiClient.cs | 10 +- .../Pages/Admin/Database/Index.cshtml.cs | 52 +- tools/run_kis_data_collection_v1.py | 2 +- 185 files changed, 2062 insertions(+), 34521 deletions(-) create mode 100644 docs/API_REFERENCE.md create mode 100644 docs/CICD_PIPELINE.md create mode 100644 docs/DEPLOYMENT_RUNBOOK.md create mode 100644 docs/DEV_WORKFLOWS.md create mode 100644 docs/MIGRATION_STATUS.md create mode 100644 docs/OMS_WMS_ERP_PLAYBOOK.md create mode 100644 docs/UI_GUIDELINES.md rename docs/{ => archive}/CICD_ANALYSIS_AND_ROADMAP.md (96%) rename docs/{ => archive}/CICD_MONITORING_GUIDE.md (95%) rename docs/{ => archive}/CI_CD_IMPLEMENTATION_SUMMARY.md (96%) rename docs/{ => archive}/DEPLOYMENT_TROUBLESHOOTING.md (95%) rename docs/{ => archive}/TESTING_AND_DEPLOYMENT.md (96%) delete mode 100644 oms-wms-erp/.dockerignore delete mode 100644 oms-wms-erp/.env.example delete mode 100644 oms-wms-erp/.eslintrc.cjs delete mode 100644 oms-wms-erp/.gitea/workflows/deploy-prod.yml delete mode 100644 oms-wms-erp/.gitea/workflows/prepare-release.yml delete mode 100644 oms-wms-erp/.github/workflows/ci.yml delete mode 100644 oms-wms-erp/.github/workflows/deploy-storybook.yml delete mode 100644 oms-wms-erp/.gitignore delete mode 100644 oms-wms-erp/.husky/pre-commit delete mode 100644 oms-wms-erp/.lintstagedrc.json delete mode 100644 oms-wms-erp/.prettierrc.json delete mode 100644 oms-wms-erp/.storybook/main.ts delete mode 100644 oms-wms-erp/.storybook/preview.ts delete mode 100644 oms-wms-erp/DEPLOYMENT.md delete mode 100644 oms-wms-erp/DEVELOPMENT.md delete mode 100644 oms-wms-erp/Dockerfile delete mode 100644 oms-wms-erp/GITHUB-SETUP.md delete mode 100644 oms-wms-erp/Makefile delete mode 100644 oms-wms-erp/PHASE1-COMPLETION.md delete mode 100644 oms-wms-erp/PHASE1-STEP2-VERIFICATION.md delete mode 100644 oms-wms-erp/PHASE2-ROADMAP.md delete mode 100644 oms-wms-erp/PHASE2-STEP3-COMPLETION.md delete mode 100644 oms-wms-erp/PHASE2-STEP4-FINAL-VERIFICATION.md delete mode 100644 oms-wms-erp/PHASE2-TYPED-FIELDS.md delete mode 100644 oms-wms-erp/PHASE3-STEP1-COMPLETION.md delete mode 100644 oms-wms-erp/PHASE3-STEP1-TYPED-FIELDS-COMPLETE.md delete mode 100644 oms-wms-erp/PHASE3-STEP2-SMART-COMPONENTS.md delete mode 100644 oms-wms-erp/PRIMITIVES-IMPLEMENTATION.md delete mode 100644 oms-wms-erp/README.md delete mode 100644 oms-wms-erp/TEST-GUIDE.md delete mode 100644 oms-wms-erp/index.html delete mode 100644 oms-wms-erp/package-lock.json delete mode 100644 oms-wms-erp/package.json delete mode 100644 oms-wms-erp/playwright.config.ts delete mode 100644 oms-wms-erp/scripts/generate-primitive.mjs delete mode 100644 oms-wms-erp/scripts/generate-store.mjs delete mode 100644 oms-wms-erp/src/App.vue delete mode 100644 oms-wms-erp/src/components/composites${component}.vue delete mode 100644 oms-wms-erp/src/components/composites/InventoryTransfer.vue delete mode 100644 oms-wms-erp/src/components/composites/OrderForm.vue delete mode 100644 oms-wms-erp/src/components/composites/index.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/AddressField/AddressField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/AddressField/AddressField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/AddressField/AddressField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/ApprovalField/ApprovalField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/ApprovalField/ApprovalField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/ApprovalField/ApprovalField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/BankAccountField/BankAccountField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/BankAccountField/BankAccountField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/BankAccountField/BankAccountField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/CustomerField/CustomerField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/DateRangeField/DateRangeField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/DateRangeField/DateRangeField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/DateRangeField/DateRangeField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/OrderLineField/OrderLineField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/OrderLineField/OrderLineField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/OrderLineField/OrderLineField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/ProductField/ProductField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/ProductField/ProductField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/ProductField/ProductField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/RoleField/RoleField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/RoleField/RoleField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/RoleField/RoleField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/StockTransferField/StockTransferField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/StockTransferField/StockTransferField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/StockTransferField/StockTransferField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/SupplierField/SupplierField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/SupplierField/SupplierField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/SupplierField/SupplierField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/TaxIDField/TaxIDField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/TaxIDField/TaxIDField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/TaxIDField/TaxIDField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/WarehouseField/WarehouseField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/WarehouseField/WarehouseField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/domain/WarehouseField/WarehouseField.vue delete mode 100644 oms-wms-erp/src/components/fields/domain/index.ts delete mode 100644 oms-wms-erp/src/components/fields/typed/CheckboxField/CheckboxField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/CurrencyField/CurrencyField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/DateField/DateField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/EmailField/EmailField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/NumberField/NumberField.spec.ts delete mode 100644 oms-wms-erp/src/components/fields/typed/NumberField/NumberField.stories.ts delete mode 100644 oms-wms-erp/src/components/fields/typed/NumberField/NumberField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/PercentageField/PercentageField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/PhoneField/PhoneField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/SelectField/SelectField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/StatusField/StatusField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/TextField/TextField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/TextareaField/TextareaField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/URLField/URLField.vue delete mode 100644 oms-wms-erp/src/components/fields/typed/index.ts delete mode 100644 oms-wms-erp/src/components/primitives/AccessibleTable.vue delete mode 100644 oms-wms-erp/src/components/primitives/Alert.vue delete mode 100644 oms-wms-erp/src/components/primitives/Button/ButtonBase.spec.ts delete mode 100644 oms-wms-erp/src/components/primitives/Button/ButtonBase.stories.ts delete mode 100644 oms-wms-erp/src/components/primitives/Button/ButtonBase.vue delete mode 100644 oms-wms-erp/src/components/primitives/FormField.vue delete mode 100644 oms-wms-erp/src/components/primitives/Input/InputBase.vue delete mode 100644 oms-wms-erp/src/components/primitives/Select/SelectBase.vue delete mode 100644 oms-wms-erp/src/components/primitives/Spinner.vue delete mode 100644 oms-wms-erp/src/components/primitives/Table/TableBase.vue delete mode 100644 oms-wms-erp/src/composables/useFormatting.ts delete mode 100644 oms-wms-erp/src/composables/useValidation.ts delete mode 100644 oms-wms-erp/src/main.ts delete mode 100644 oms-wms-erp/src/router.ts delete mode 100644 oms-wms-erp/src/router/admin-routes.ts delete mode 100644 oms-wms-erp/src/router/guards.ts delete mode 100644 oms-wms-erp/src/router/index.ts delete mode 100644 oms-wms-erp/src/services/api/client.ts delete mode 100644 oms-wms-erp/src/services/mockApi.ts delete mode 100644 oms-wms-erp/src/stores/index.ts delete mode 100644 oms-wms-erp/src/stores/modules/auth.ts delete mode 100644 oms-wms-erp/src/stores/modules/customers.ts delete mode 100644 oms-wms-erp/src/stores/modules/inventory.ts delete mode 100644 oms-wms-erp/src/stores/modules/orders.ts delete mode 100644 oms-wms-erp/src/stores/modules/pricing.ts delete mode 100644 oms-wms-erp/src/stores/modules/products.ts delete mode 100644 oms-wms-erp/src/stores/modules/reports.ts delete mode 100644 oms-wms-erp/src/stores/modules/settings.ts delete mode 100644 oms-wms-erp/src/stores/modules/suppliers.ts delete mode 100644 oms-wms-erp/src/stores/modules/users.ts delete mode 100644 oms-wms-erp/src/stores/modules/warehouses.ts delete mode 100644 oms-wms-erp/src/style.css delete mode 100644 oms-wms-erp/src/types/domains.ts delete mode 100644 oms-wms-erp/src/types/schemas.ts delete mode 100644 oms-wms-erp/src/utils/errors.ts delete mode 100644 oms-wms-erp/src/utils/fieldTypes.ts delete mode 100644 oms-wms-erp/src/views/Customer/CustomerForm.vue delete mode 100644 oms-wms-erp/src/views/Customer/CustomerList.vue delete mode 100644 oms-wms-erp/src/views/Home.vue delete mode 100644 oms-wms-erp/src/views/Inventory/InventoryList.vue delete mode 100644 oms-wms-erp/src/views/LoginPage.vue delete mode 100644 oms-wms-erp/src/views/NotFound.vue delete mode 100644 oms-wms-erp/src/views/Order/OrderList.vue delete mode 100644 oms-wms-erp/src/views/Product/ProductList.vue delete mode 100644 oms-wms-erp/src/views/Warehouse/WarehouseForm.vue delete mode 100644 oms-wms-erp/src/views/Warehouse/WarehouseList.vue delete mode 100644 oms-wms-erp/src/views/admin${form}.vue delete mode 100644 oms-wms-erp/src/views/admin${page}.vue delete mode 100644 oms-wms-erp/src/views/admin/Dashboard.vue delete mode 100644 oms-wms-erp/src/views/admin/OrderForm.vue delete mode 100644 oms-wms-erp/src/views/admin/ProductForm.vue delete mode 100644 oms-wms-erp/src/views/admin/index.ts delete mode 100644 oms-wms-erp/tests/e2e/authentication.spec.ts delete mode 100644 oms-wms-erp/tests/e2e/complete-flow.spec.ts delete mode 100644 oms-wms-erp/tests/e2e/form-submission.spec.ts delete mode 100644 oms-wms-erp/tests/e2e/full-navigation.spec.ts delete mode 100644 oms-wms-erp/tests/e2e/index.ts delete mode 100644 oms-wms-erp/tests/e2e/inventory-flow.spec.ts delete mode 100644 oms-wms-erp/tests/e2e/order-flow.spec.ts delete mode 100644 oms-wms-erp/tests/integration/customer-workflow.spec.ts delete mode 100644 oms-wms-erp/tests/integration/index.ts delete mode 100644 oms-wms-erp/tests/integration/inventory-workflow.spec.ts delete mode 100644 oms-wms-erp/tests/integration/inventory.spec.ts delete mode 100644 oms-wms-erp/tests/integration/order-workflow.spec.ts delete mode 100644 oms-wms-erp/tests/integration/orders.spec.ts delete mode 100644 oms-wms-erp/tests/integration/products.spec.ts delete mode 100644 oms-wms-erp/tests/mocks/handlers.ts delete mode 100644 oms-wms-erp/tests/mocks/server.ts delete mode 100644 oms-wms-erp/tests/setup.ts delete mode 100644 oms-wms-erp/tsconfig.json delete mode 100644 oms-wms-erp/vite.config.ts delete mode 100644 oms-wms-erp/vitest.config.ts delete mode 100644 src/dotnet/QuantEngine.Infrastructure/External/KisApiClient.cs diff --git a/.gitignore b/.gitignore index fcb282a9..cb76d9e7 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,12 @@ src/dotnet/QuantEngine.Web/wwwroot/_framework/ # 런타임 감사 로그 (append-only, 매 DAG 실행마다 증가) runtime/lineage_events.jsonl +# .NET 런타임 로그 (Serilog 등, 실행마다 재생성) +**/logs/*.log + +# Playwright 테스트 산출물 (스크린샷/트레이스, 실행마다 재생성) +test-results/ + # OS / 에디터 ... .DS_Store diff --git a/CLAUDE.md b/CLAUDE.md index b1664300..50569cbe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,6 +19,13 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co │ ├─ Emergency password reset, auth endpoints │ │ └─ Portfolio/settings endpoints │ ├─────────────────────────────────────────────────────────────┤ +│ Application Layer (QuantEngine.Application) │ +│ ├─ ~22 orchestrators/services between Web and Infrastructure│ +│ ├─ KisDataCollectionOrchestrator, PipelineOrchestrator, │ +│ │ WorkspaceService, etc. │ +│ └─ Owns cross-cutting workflows; Web/Pages call these, not │ +│ Infrastructure directly │ +├─────────────────────────────────────────────────────────────┤ │ Repository Layer (Dapper ORM, SQL-driven) │ │ ├─ IWorkspaceRepository: workspace/settings CRUD │ │ ├─ ICollectionRepository: KIS runs, snapshots, errors │ @@ -56,7 +63,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co │ ├─ Unified schema: quantenginedb / quantengine schema │ │ ├─ Key tables: kis_tokens, kis_collection_runs/snapshots │ │ │ workspaces, account_snapshots, settings │ -│ ├─ Auto-migrations via DbUp (V001.sql, V002.sql, ...) │ +│ ├─ Auto-migrations via DbUp (V1__Name.sql, V2__Name.sql...) │ │ └─ Documented in: docs/db/quantengine.dbml (DBML sync) │ ├─────────────────────────────────────────────────────────────┤ │ Validation & Quality Gates (Python) │ @@ -96,366 +103,54 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - **Architecture**: .NET 9 + C# (web UI + APIs, primary); Python (legacy data collection, gradual deprecation) - **Critical Path**: KIS API → PostgreSQL → Admin Dashboard (read-only quotations only, no trading APIs) - **Validation**: 30+ contract validators + 9 parallel CI jobs + manual release gates -- **Web UI**: Blazor Interactive WebAssembly (MudBlazor) + ASP.NET Core Web API (API-First) +- **Web UI**: Razor Pages (Server-Rendered) + Bootstrap 5/Tabler + Cookie auth (Blazor/MudBlazor deprecated 2026-07-11 — see UI Design Principles below) - **Database**: PostgreSQL (Npgsql 8.0), single unified database - **Data Source**: KIS Open API (quotations/ranking read-only), with fallbacks - **Key Runtimes**: .NET 9, Python 3.9+, Node.js 16+ -### Migration Phases Status (2026-07-11) +### Migration Status Summary +- **Phase 1** (Web UI → Razor Pages): ✅ Complete (2026-07-11) +- **Phase 2** (KIS Data Collection Pipeline): 95% complete — pipeline orchestration (Python→.NET) pending +- **Phase 3** (Node.js→.NET CLI Tools): Planned +- **Phase 4** (CI/CD Pipeline Hardening): 80% complete — Gitea Actions runner has network limitations, manual-trigger release workflow active (see Deployment & Operations) +- **Phase 5** (Admin UI & Deployment Optimization): ✅ Complete (2026-07-11) -**Phase 1: Web UI Migration** ✅ 완료 (2026-07-11) -- **새로운 표준**: Razor Pages (Server-Rendered) + Cookie Authentication + Tabler UI -- **폐기 대상**: Blazor Interactive WebAssembly, MudBlazor, SmartAdmin -- **완료 기준 — Phase 1 Success Criteria**: - - ✅ Cookie 인증 구현 (AuthService + IpLockoutService + BCrypt) - - ✅ Razor Pages 렌더링 (Admin 레이아웃 + 3개 이상 기본 페이지) - - ✅ 공용 UI 컴포넌트 (4개 이상 shared partials) - - ✅ 보안: 백도어 제거, 무솔트 해시 마이그레이션, IP 잠금 - - ✅ 빌드 성공: 0 errors, 0 warnings - - ✅ CLAUDE.md 업데이트 (UI 기준 + 인증 정책) - - **✅ 모든 기준 충족됨** (2026-07-11) -- **구현 완료**: - - ✅ Cookie 기반 인증 (AuthService + IpLockoutService) - - ✅ Razor Pages CRUD 레이아웃 (_AdminLayout.cshtml, shared partials) - - ✅ Admin 페이지: Dashboard, Collection, Users (기본 구조) - - ✅ 공용 UI 컴포넌트: _ValidationSummary, _Pagination, _StatusBadge, _EmptyState - - ✅ 보안 개선: BCrypt 해싱, IP 잠금, 하드코딩된 백도어 제거 - - ✅ 빌드: 0 errors, 0 warnings (Newtonsoft.Json 보안 경고 제외) - - ✅ CLAUDE.md 완전 업데이트 (UI 기준, 인증, 상태 정의) -- **구현 미완료 (향후 작업)**: - - 🔄 Users 페이지: Create/Edit 폼 완성 - - 🔄 Collection 페이지: 스냅샷/에러 조회 상세화 - - 🔄 E2E 테스트: Playwright 스펙 업데이트 +Full phase-by-phase history and detail: [docs/MIGRATION_STATUS.md](docs/MIGRATION_STATUS.md) -**Phase 2: KIS Data Collection Pipeline** ✅ 95% COMPLETE -- ✅ KIS API Client: Full implementation complete - - IKisApiClient interface (5 quotation methods) - - KisApiClient with real HTTP implementation + token caching - - All governance rules enforced (no trading APIs) - - Windows env var + registry fallback for credentials - - Build: 0 errors, 0 warnings -- ✅ PostgreSQL Infrastructure: Complete - - PostgresTokenCache (token management, 10-min skew) - - CollectionRepository (full CRUD + dashboard aggregations) - - Auto-creates kis_tokens, kis_collection_runs, kis_collection_snapshots, kis_collection_errors - - Dapper ORM + parameterized SQL (injection-proof) -- ✅ Web API Endpoints: Complete - - CollectionEndpoints (6 endpoints: state, runs, snapshots, errors, latest, start) - - ApiClient for Blazor consumption -- ✅ Blazor UI: Complete - - Collection.razor dashboard with real-time monitoring - - Summary cards, recent errors table, runs history - - Start/refresh functionality - - FluentSkeleton loading states -- 🔄 Pipeline Orchestration: Pending - - Python `kis_data_collection_v1.py` → .NET (data fetching + validation) - - Real KIS API data collection workflow integration - - E2E test: API → DB → UI validation +## Deployment & Operations -**Phase 3: Node.js→.NET CLI Tools** 📋 PLANNED -- Makefile created (npm → make mappings) -- np operations documented - -**Phase 4: CI/CD Pipeline Hardening** ✅ 80% COMPLETE (2026-07-11) -- ✅ deploy-prod.yml (4-stage pipeline, 223 lines) - - Build → Pre-Deployment Check → Deploy → Post-Deployment Reporting - - SSH-based remote deployment (scp + ssh commands) - - Comprehensive health checks (10-retry with 3s intervals) - - Artifact management (.tar.gz) -- ✅ Workflow consolidation (2 active files) - - ci.yml: PR validation only (maintains 29 validators) - - deploy-prod.yml: Production deployment - - Deleted: merge-to-main.yml (non-functional), fast-validation.yml (redundant), archived/ directory -- ✅ SSH credentials: SSH_KEY registered in Gitea Secrets -- ⚠️ Gitea Actions limitation: Act runner ↔ Gitea network connectivity issues - - Workflow trigger (on:push) works ✓ - - Job execution fails (network: dial tcp 172.18.0.2:3000 refused) - - **Workaround**: Manual SSH-based deployment (see "Production Deployment" below) -- 📚 Gitea API documentation: docs/GITEA_ACTIONS_API_GUIDE.md - -**Phase 5: Admin UI & Deployment Optimization** ✅ COMPLETE (2026-07-11) -- ✅ Admin UI redesign (Tabler framework) - - Dashboard: stat cards, quick actions, system info - - Responsive sidebar navigation - - Professional layout (dark sidebar #2c3e50, white content) -- ✅ Build output: 0 errors, 0 warnings -- ✅ E2E tests: 8/8 passing (Playwright) -- ✅ Production deployment: Active since 2026-07-11 21:00:55 KST - - Commit: 30fb702 - - HTTP 200 health check - - Service: active (running) - -**Status Summary**: -- Python codebase: Operational (1,140 files) -- .NET 9 coverage: Core (✅), Infrastructure (✅), API (✅), Web UI (✅) -- Database: PostgreSQL fully migrated -- CI/CD: Manual SSH deployment (fully operational), Gitea Actions (limited by infrastructure) -- Release gates: Python gates remain authority until Phase 2 integration testing complete - -## Deployment & Operations (Phase 4-5, 2026-07-11) - -**Production Server**: Hetzner Cloud `178.104.200.7` (kjh2064@178.104.200.7) - -Projects on server: -1. **TaxBaik** (홈페이지) — Nginx location `/taxbaik` -2. **QuantEngine** (데이터 수집/분석) — Nginx location `/quantengine` +**Production Server**: Hetzner Cloud `178.104.200.7` (kjh2064@178.104.200.7). Full runbook +(release/deploy workflow, SSH key setup, health checks, rollback, troubleshooting): +[docs/DEPLOYMENT_RUNBOOK.md](docs/DEPLOYMENT_RUNBOOK.md) ### ⚠️ CRITICAL: CI/CD-Only Deployment Mandate -**Rule**: ALL production deployments MUST go through Gitea Actions CI/CD. Manual SSH deployments are **FORBIDDEN**. - -**Why**: -- Automatic validation (build, health checks, version verification) -- Audit trail (all deployments logged in Gitea Actions) -- Consistent process (no manual errors) -- Rollback safety (deployment history retained) -- Release traceability (version control via git tags) +**Rule**: ALL production deployments MUST go through Gitea Actions CI/CD +(`prepare-release.yml` → `deploy-prod.yml`, both manual `workflow_dispatch`). Manual SSH +deployments are **FORBIDDEN** (audit trail, consistent health checks, rollback safety). ### ⚠️ CRITICAL: DB Secret Management (Incident 2026-07-12) -**Incident**: `quant.taxbaik.com/login`이 `28P01 password authentication failed`로 장애 발생. -원인: `appsettings.Production.json`에 하드코딩되어 배포된 DB 비밀번호가, 실제 DB 비밀번호가 -로테이션된 이후에도 계속 옛날 값(심지어 이전 세션에서 검증 없이 넣은 placeholder였던 적도 있음) +**Incident**: `quant.taxbaik.com/login`이 `28P01 password authentication failed`로 장애 발생 — +`appsettings.Production.json`에 하드코딩되어 배포된 DB 비밀번호가 로테이션 이후에도 옛날 값 그대로 배포되고 있었음. **Rule**: **DB 접속 문자열(`ConnectionStrings`)은 절대 `appsettings.Production.json`이나 -워크플로우 파일에 하드코딩하지 않는다.** `prepare-release.yml`이 생성하는 -`appsettings.Production.json`에는 `Logging` 설정만 있고 `ConnectionStrings`는 없다 — -이는 의도된 설계다 (Gitea Release는 누구나 다운로드 가능한 아티팩트이므로 시크릿을 -담으면 안 됨). +워크플로우 파일에 하드코딩하지 않는다.** `prepare-release.yml`이 생성하는 아티팩트에는 +`Logging` 설정만 있고 `ConnectionStrings`는 없다 (의도된 설계 — Gitea Release는 누구나 +다운로드 가능하므로 시크릿을 담으면 안 됨). -**실제 DB 비밀번호의 출처**: 프로덕션 서버의 `/home/kjh2064/.config/quantengine.env` -파일 (`ConnectionStrings__DefaultConnection=...` 형식) 하나뿐이며, -`quantengine.service.d/env.conf` drop-in의 `EnvironmentFile=` 지시자로 systemd가 -이 값을 환경변수로 주입한다. ASP.NET Core 설정 우선순위상 **환경변수가 -`appsettings.Production.json`을 오버라이드**하므로, 배포되는 아티팩트 자체에는 -DB 정보가 없어도 서비스는 정상 동작한다. +실제 DB 비밀번호는 프로덕션 서버의 `/home/kjh2064/.config/quantengine.env` 파일에만 존재하며, +`quantengine.service.d/env.conf`의 `EnvironmentFile=` 지시자로 systemd가 환경변수로 주입한다 +(환경변수가 `appsettings.Production.json`을 오버라이드). -**DB 비밀번호가 바뀌면** (로테이션 등): `/home/kjh2064/.config/quantengine.env` 파일만 -갱신하고 `sudo systemctl restart quantengine`. 워크플로우 파일이나 Gitea Secrets는 -건드릴 필요 없음 (배포 파이프라인은 DB 비밀번호를 모른 채로 동작해야 정상). +**DB 비밀번호 로테이션 시**: `/home/kjh2064/.config/quantengine.env`만 갱신 + +`sudo systemctl restart quantengine`. 워크플로우 파일/Gitea Secrets는 건드리지 않음. -**배포 전 체크리스트에 추가**: -- ✅ 새 릴리즈 배포 후 반드시 `/Account/Login` 실제 HTTP 응답 + `journalctl -u quantengine`에서 - `28P01`/`password authentication failed` 부재 확인 (단순 프로세스 `active` 상태만으로는 - DB 연결 실패를 못 잡음 — ASP.NET Core는 DB 없이도 기동은 되고 로그인 요청 시점에야 실패함) -- ✅ `.config/quantengine.env`의 존재와 `quantengine.service.d/env.conf`의 - `EnvironmentFile=` 배선이 서버에 유지되고 있는지 (systemd unit 자체를 재생성/덮어쓰는 - 배포 방식으로 전환할 경우 이 drop-in이 날아가지 않는지 확인 필요) - -### Production Deployment Strategy (Release-Based) - -**Architecture**: Two-Workflow System (Release Creation → Deployment) - -#### Workflow 1: prepare-release.yml (Release Creation) - -**Purpose**: Create a release with built artifact - -**Trigger**: Manual (`workflow_dispatch`) -```bash -# Visit Gitea Actions and select prepare-release.yml -# Input version: v0.1.20260711 (or any semantic version) -``` - -**What it does**: -1. ✓ Build (restore, build, publish) -2. ✓ Generate `appsettings.Production.json` -3. ✓ Package artifact: `.tar.gz` -4. ✓ Create git tag: `v0.1.20260711` -5. ✓ Create Gitea Release with artifact attached -6. ✓ Notify: Release ready for deployment - -**Output**: Gitea Release with downloadable artifact - -#### Workflow 2: deploy-prod.yml (Deployment) - -**Purpose**: Deploy a release to production - -**Trigger**: Manual (`workflow_dispatch`) -```bash -# Visit Gitea Actions and select deploy-prod.yml -# Input release: v0.1.20260711 (optional — uses latest if empty) -``` - -**What it does**: -1. ✓ Fetch Release (from Gitea Releases) -2. ✓ Download artifact -3. ✓ Verify SSH credentials -4. ✓ Upload to production server -5. ✓ Extract and symlink -6. ✓ Restart service -7. ✓ 6-point health checks -8. ✓ Report deployment status - -**Deployment Pipeline (5 Stages)**: - -| Stage | Purpose | Timeout | -|-------|---------|---------| -| 1. Fetch Release | Query Gitea Releases, download artifact | 10min | -| 2. Pre-Check | Verify SSH keys, secrets, release | 5min | -| 3. Deploy | Upload, extract, symlink, restart service | 30min | -| 4. Health Check | 6-point verification (HTTP, CSS, login, service, release, DB auth) | 10min | -| 5. Report | Final deployment status | Auto | - -**Health Checks (Automatic)**: -- ✓ HTTP 200 on `/Account/Login` -- ✓ Login page content verification -- ✓ CSS file loads (`/css/admin.css`) -- ✓ Service status (systemctl active) -- ✓ Release verification (deployed release tag matches) -- ✓ **DB authentication check** (`journalctl`에서 `28P01`/`password authentication failed` - 부재 확인 — GET `/Account/Login`은 DB가 끊겨도 200을 반환하므로 이 체크가 없으면 - DB 장애를 배포 파이프라인이 놓친다. 2026-07-12 사고 이후 추가됨) - -**Complete Deployment Flow**: -``` -1. Code committed to main branch -2. Create release: prepare-release.yml workflow_dispatch (manual) - → Builds code - → Creates Gitea Release with artifact - → Tags repository -3. Deploy release: deploy-prod.yml workflow_dispatch (manual) - → Selects release version - → Downloads artifact from Gitea Release - → Deploys to production server - → Runs health checks - → Reports status -``` - -### Pre-Deployment Checklist - -**Before creating a release**, verify: -1. ✅ Local build: `dotnet build src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release` (0 errors, 0 warnings) -2. ✅ E2E tests pass: `npx playwright test` -3. ✅ Admin pages verified (200 status, no 500 errors) -4. ✅ All changes committed and pushed to main branch -5. ✅ No uncommitted changes: `git status` - -### Release & Deployment Workflow - -**Step 1: Create Release (prepare-release.yml)** -```bash -# Visit Gitea Actions -# https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/actions - -# Run prepare-release.yml workflow -# Input: version = v0.1.20260711 - -# Workflow will: -# - Build and publish -# - Package artifact -# - Create git tag -# - Create Gitea Release -# - Attach artifact -``` - -**Step 2: Deploy Release (deploy-prod.yml)** -```bash -# Visit Gitea Actions (same page) -# Run deploy-prod.yml workflow -# Input: release = v0.1.20260711 (leave empty for latest) - -# Workflow will: -# - Download artifact from release -# - Deploy to production server -# - Run health checks -# - Report status -``` - -### SSH Key Configuration (Required) - -**Setup (One-time)**: -1. Generate ED25519 key locally (or reuse existing): - ```bash - ssh-keygen -t ed25519 -f ~/.ssh/quantengine_deploy -C "QuantEngine CI/CD" - ``` - -2. Add public key to production server: - ```bash - ssh-copy-id -i ~/.ssh/quantengine_deploy.pub kjh2064@178.104.200.7 - ``` - -3. Get private key in base64 format: - ```bash - # macOS/Linux - base64 -w 0 ~/.ssh/quantengine_deploy > /tmp/key_b64.txt - cat /tmp/key_b64.txt | pbcopy - - # Or Windows PowerShell - $key = Get-Content ~/.ssh/quantengine_deploy -Raw - [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($key)) | Set-Clipboard - ``` - -4. Configure in Gitea: - - URL: https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/settings/secrets - - Add secret: `DEPLOY_SSH_KEY_B64` (base64-encoded private key) - - Or: `DEPLOY_SSH_KEY` (raw PEM format) - - Also add: `GITEA_TOKEN` (for release API access) - - Generate at: https://gitea.taxbaik.com/user/settings/applications - - Required permissions: `repo` + `read:actions` - -### Deployment Monitoring - -**During Deployment**: -- Watch live in Gitea Actions UI -- Jobs complete in order: Build → Pre-Check → Deploy → Health Check → Report - -**After Deployment**: -```bash -# SSH into server -ssh kjh2064@178.104.200.7 - -# Check active deployment -readlink ~/quantengine_active - -# View service status -systemctl status quantengine - -# Tail live logs -journalctl -u quantengine -f - -# Health check -curl -I http://127.0.0.1:5000/Account/Login -``` - -### Automatic Rollback (if health check fails) - -If health check fails, deployment stops automatically: -1. Service restart may fail -2. Symlink update reverts to previous deployment -3. Gitea Actions marks deployment as FAILED -4. Logs include failure details - -Manual rollback (if needed): -```bash -# List deployments -ls -lht ~/deployments/quantengine_* - -# Revert symlink to previous version -ln -sfn /home/kjh2064/deployments/quantengine_YYYYMMDD_HHMMSS_COMMIT ~/quantengine_active - -# Restart service -sudo systemctl restart quantengine - -# Verify -curl http://127.0.0.1:5000/Account/Login -``` - -### Troubleshooting Deployment Failures - -**Issue**: Build fails -- Check: `dotnet build` locally first -- Ensure: No compilation errors, 0 warnings - -**Issue**: Health check timeout -- Check: Service logs: `journalctl -u quantengine -n 50` -- Check: Port 5000 listening: `ss -tlnp | grep 5000` -- Check: DB connectivity in appsettings.Production.json - -**Issue**: SSH key error -- Verify: `DEPLOY_SSH_KEY_B64` or `DEPLOY_SSH_KEY` in Gitea Secrets -- Check: Public key added to `~/.ssh/authorized_keys` on server -- Test: `ssh -i ~/.ssh/key_file kjh2064@178.104.200.7 echo OK` +**배포 후 필수 확인**: `/Account/Login` 실제 HTTP 응답 + `journalctl -u quantengine`에서 +`28P01`/`password authentication failed` 부재 확인 (단순 `active` 상태만으로는 DB 연결 실패를 +못 잡음 — ASP.NET Core는 DB 없이도 기동은 되고 로그인 요청 시점에야 실패함). 6-point 헬스체크에 +DB 인증 확인이 포함되어 있음. 상세: [docs/DEPLOYMENT_RUNBOOK.md](docs/DEPLOYMENT_RUNBOOK.md) ### Git Repository @@ -463,149 +158,109 @@ curl http://127.0.0.1:5000/Account/Login - **HTTP**: `https://gitea.taxbaik.com/kjh2064/QuantEngineByItz.git` - **SSH**: `ssh://git@gitea.taxbaik.com:2222/kjh2064/QuantEngineByItz.git` -## UI Design Principles (2026-07-11 — Migrated to Razor Pages) +## UI Design Principles -### Framework & Design System (NEW — 2026-07-11) +- **Primary Framework**: ASP.NET Core Razor Pages (server-rendered, no WASM) + Bootstrap 5 + + Tabler UI. Blazor Interactive WebAssembly, MudBlazor, SmartAdmin are **deprecated** + (2026-07-11). The `.sln` has 6 projects: Core, Application, Infrastructure, Web, Tools, + Core.Tests — there is no `QuantEngine.Web.Client` in the repo (fully removed, not just + excluded from the solution; verified 2026-07-30). +- **Authentication**: Cookie (`QuantEngine.Admin.Auth`, HttpOnly, SameSite=Lax, 12h sliding) + + BCrypt + IP lockout (3 strikes/15min). Folder-level `[Authorize]` via + `AuthorizeFolder("/Admin")` — no per-page repetition. Login: `/Account/Login`. +- **Component rules**: PageModel constructor DI (repos/services directly, no API hop); + `OnPostAsync()`/`OnPostDeleteAsync()` multi-handler pattern; validation failures return + `Page()`; DataAnnotations + FluentValidation double-check. +- Full component-to-Bootstrap/Tabler mapping table (buttons, inputs, tables, badges, modals, + validation messages): [docs/UI_GUIDELINES.md](docs/UI_GUIDELINES.md) -- **Primary Framework**: ASP.NET Core Razor Pages + Bootstrap 5 + Tabler UI -- **Design System**: Tabler (Bootstrap 5 기반), 밀집 레이아웃 + 전통 서버 렌더링 -- **Render Mode**: **Server-side Razor Pages** — 모든 Admin UI는 서버에서 렌더링, Cookie 기반 인증 (API-First WASM 폐기) -- **Authentication**: Cookie Authentication (HttpOnly) + BCrypt password hashing + IP lockout (3 strikes, 15-min) -- **Deprecation**: **Blazor Interactive WebAssembly 폐기**, **MudBlazor 컴포넌트 폐기** (2026-07-11), **SmartAdmin 폐기**. 기존 WASM 코드는 `/QuantEngine.Web.Client` 폴더에 참고용으로만 보관 (`.sln`에서 제외) +## Development Commands -### Component Development Rules (NEW) +### .NET +```powershell +cd src/dotnet +dotnet restore +dotnet build # Debug (0 errors, 0 warnings) +dotnet build -c Release +dotnet watch run --project QuantEngine.Web # Hot-reload → http://localhost:5265 +dotnet test src/dotnet/QuantEngine.Core.Tests -c Release +dotnet test src/dotnet/QuantEngine.sln --configuration Release +``` -1. **All Admin UI Development** (New + Refactored): - - Use **Razor Pages** (.cshtml + .cshtml.cs PageModel) exclusively for admin - - UI는 Repository/Service를 생성자 DI로 직접 호출 (API 홉 없음) - - Bootstrap 5 + Tabler UI CSS classes for styling - - **Form Validation**: DataAnnotations DTO + FluentValidation IValidator 이중 검증 - - HTML `
` + tag helpers (`asp-for`, `asp-action`, `asp-page`) - -2. **Authentication & Authorization**: - - Cookie name: `QuantEngine.Admin.Auth` (HttpOnly, SameSite=Lax) - - Session duration: 12 hours (sliding expiration) - - Folder-level `[Authorize]` via `AuthorizeFolder("/Admin")` convention (per-page 반복 금지) - - Login: `/Account/Login` (Razor Page, NO WASM) - - Password: BCrypt-hashed (auto-migrates existing SHA-256 hashes on first login) - - IP Lockout: 3 failed attempts → 15-minute lockout - -3. **Data & Form Patterns**: - - PageModel constructor: `public IndexModel(IWorkspaceRepository repo, ILogger logger)` - - Form submission: `OnPostAsync()` / `OnPostDeleteAsync()` (multi-handler pattern) - - Validation failures: return `Page()` (re-render with ModelState errors) - - Pagination: `PaginationModel` record (Page, TotalPages, Func BuildPageUrl) - - Empty states: `` - -4. **Component Mapping** (Bootstrap 5 + Tabler): - -| UI Element | Component | Notes | -|-----------|-----------|-------| -| Button | `