Commit Graph

674 Commits

Author SHA1 Message Date
Ho Ngoc Hai
36a0a9c256 feat: Add functional tests for MktZaloService, new contract and load tests, and audit documentation, while removing a legacy infrastructure project and updating service configurations. 2026-03-25 15:00:05 +07:00
Ho Ngoc Hai
7a752f4a82 fix(qa): resolve build failures and test issues found in QA verification
- packages/logger: upgrade tsconfig target to ES2022 to support Array.at()
- packages/http-client: exclude test files from tsc build to prevent noUnusedLocals errors
- packages/http-client/test: use vi.hoisted() for mock functions (vi.mock hoisting fix)
- services/goodgo-mcp-server/tests: use vi.hoisted() for all 4 test files (catalog, inventory, analytics, recipe)
- web-client-tpos: remove stale @using WebClientTpos.Client.Components.Auth from 10 auth pages (moved to blazor-ui RCL)
- web-client-tpos: remove AttachToken() calls in PosDataService (auth via BFF httpOnly cookie)
- web-client-tpos: fix IamApiService.SetAuthHeader() and MerchantApiService.AttachTokenAsync() — make no-op, remove _auth dependency
- web-client-tpos: fix Profile.razor — remove AttachToken() method and calls
- web-client-tpos: fix OnboardingReady.razor — escape @keyframes → @@keyframes in Razor style block
- web-client-tpos: fix PosDataService.GetListFromApiAsync() — check array before property lookup to fix plain array deserialization
- web-client-tpos/tests: update AuthStateServiceTests to new AuthStateService.Login(email, role) signature (no token param)
- web-client-tpos/tests: update PosDataServiceTests to new PosDataService(http) constructor (no authState param)

All 113 Node.js tests pass. All 30 .NET component tests pass. All .NET builds succeed.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 12:07:58 +07:00
Ho Ngoc Hai
a8edfd1597 fix(p2): Wave 3 — fix 4 P2 backend architecture issues (TEC-261)
BACK-I-01: Add CI steps to generate openapi.yaml for all 24 .NET services
- Add .config/dotnet-tools.json with swashbuckle.aspnetcore.cli 7.2.0
- Add scripts/ci/generate-openapi.sh reusable script
- Update all 24 service CI workflows with dotnet tool restore + swagger tofile + artifact upload

BACK-I-02: Add OpenTelemetry Metrics + Prometheus /metrics to _template_dot_net
- Add OTel packages (Extensions.Hosting, Instrumentation.AspNetCore, Runtime, Prometheus)
- Register AddOpenTelemetry().WithMetrics() with ASPNetCore + Runtime instrumentation
- Map MapPrometheusScrapingEndpoint("/metrics") in middleware pipeline

BACK-W-01: Remove IHttpContextAccessor from all 18 handler files in merchant-service-net
- Create MerchantBaseController abstract base with GetCurrentUserId() helper
- Add Guid UserId to 11 Commands and 7 Queries
- Remove IHttpContextAccessor injection from all handlers, use request.UserId instead
- Update 7 controllers to inherit MerchantBaseController and extract userId from JWT claims
- Remove AddHttpContextAccessor() registration from Program.cs

BACK-W-03: Add explicit commandTimeout:5 to all Dapper queries in order-service-net
- 14 files updated: QueryAsync, ExecuteScalarAsync, QueryFirstOrDefaultAsync all get commandTimeout: 5

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 10:09:45 +07:00
Ho Ngoc Hai
90434acbde fix(security): Wave 2 — fix 8 P1 frontend security & reliability issues
SEC-W-11: Remove hardcoded OAuth2 client_id/client_secret from Blazor WASM.
  - Create BffAuthController (POST /api/bff/auth/login|logout, GET /api/bff/auth/session)
  - BFF exchanges credentials with IS4 using server-side config (IdentityServer:ClientId/Secret)
  - Add IdentityServer config block to appsettings.json / appsettings.Development.json

SEC-W-12: Migrate password grant — token exchange now happens server-side in BFF, not WASM.
  - AuthService.LoginAsync() POSTs to /api/bff/auth/login (no IS4 call from WASM)

SEC-W-01: JWT in localStorage — migrate to httpOnly SameSite=Strict BFF session cookie.
  - BffAuthController sets cookie on login, clears on logout
  - AuthStateService no longer stores raw token (Token property removed)
  - AuthService only stores non-sensitive metadata (email, role) in localStorage
  - TryRestoreSessionAsync now calls GET /api/bff/auth/session instead of localStorage
  - AuthForwardingHandler reads token from bff_session cookie (legacy header fallback kept)

FRONT-W-01: Token refresh not implemented — add TokenExpiry tracking + proactive refresh timer.
  - AuthStateService: add TokenExpiry, OnTokenExpiring event, IDisposable Timer
  - Login() schedules a Timer that fires OnTokenExpiring 2 min before expiry

FRONT-W-02: DefaultRequestHeaders race condition — use per-request HttpRequestMessage.
  - PosDataService: remove AttachToken() (mutated shared DefaultRequestHeaders)
  - All HTTP helpers (PostAsync, PutAsync, PostAndGetAsync, GetListFromApiAsync,
    GetObjectFromApiAsync) now use HttpRequestMessage per-request
  - Auth handled automatically by browser cookie (same-origin, httpOnly BFF cookie)

FRONT-C-04: No route guard on AdminLayout — add auth redirect.
  - AdminLayout.OnAfterRenderAsync: after TryRestoreSessionAsync, redirect to /auth/login
    if still unauthenticated (with returnUrl param)

FRONT-C-05: shopId not validated against user permissions — add BFF verification.
  - AdminLayout: call PosData.GetShopByIdAsync(shopId) after detecting shop context
  - Redirect to /admin if BFF returns null (403/404 = no access, prevents IDOR)
  - Populate _shopName/_shopCategory from verified backend data (not just URL)

SEC-W-13: No CDN SRI for Lucide icons — add integrity hash + crossorigin attribute.
  - index.html: add integrity="sha256-NBFpKCDLjUdUP2lJaqJf1gOjWPRJgEb0HFCKWjNCIQ4="
    crossorigin="anonymous" to lucide@0.468.0 script tag

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 09:57:11 +07:00
Ho Ngoc Hai
619a06fafe fix(security): remove external Redis/MinIO/SMTP credentials from base appsettings.json
SEC-C-01 extended gap: 3 base appsettings.json files still referenced external
infrastructure (167.114.174.113) with Velik@2026 credentials and real SMTP
password — missed by the Wave 1 security fix which targeted DB credentials only.

Changes:
- iam-service-net/appsettings.json: Redis localhost (removed Velik@2026),
  SMTP localhost:1025 (removed Mailgun credentials)
- membership-service-net/appsettings.json: Redis localhost (removed Velik@2026)
- storage-service-net/appsettings.json: MinIO→localhost:9000 minioadmin/minioadmin,
  Redis→localhost (removed Velik@2026)

All production credentials (Redis, MinIO, SMTP) must be injected via
environment variables. Base appsettings.json targets docker-compose local stack.

CTO review finding: Redis__Password, MinIO:SecretKey, Email:SmtpPassword
must never appear in committed config files.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 09:54:59 +07:00
Ho Ngoc Hai
7b92332710 fix(devops): resolve 4 P2 DevOps improvements (Wave 3 — TEC-263)
- DEVOPS-W-01: Add oliver006/redis_exporter to docker-compose.yml so
  the existing prometheus.yml scrape job (redis-exporter:9121) resolves
- DEVOPS-W-04: Add redis-sentinel.yaml with Redis Sentinel HA setup
  (1 master StatefulSet + 2 replica StatefulSet + 3 sentinel pods)
  replacing the single-instance SPOF redis.yaml in staging K8s
- DEVOPS-W-05: Add network-policy.yaml with default-deny-all NetworkPolicy
  + explicit allow rules for inter-service, Traefik ingress, Redis access,
  Prometheus scrape, and external egress (Neon PostgreSQL, AMQP)
- DEVOPS-M-01: Add aquasecurity/trivy-action to docker-build.yml to scan
  every built image for CRITICAL/HIGH CVEs; results uploaded to GitHub
  Security tab via SARIF

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 09:54:32 +07:00
Ho Ngoc Hai
dd57cff6b1 fix(security): remove residual Neon/external credentials from appsettings.Development.json
SEC-C-01 gap: Security engineer's Wave 1 fix replaced Neon credentials in
appsettings.json (19 files) but missed 4 appsettings.Development.json files
that still pointed to cloud infrastructure with production credentials.

Changes per service:
- iam-service-net: DB→localhost, Redis→localhost (removed Velik@2026),
  Email SMTP→localhost:1025 (removed Mailgun password)
- membership-service-net: DB→localhost, Redis→localhost
- promotion-service-net: DB→localhost
- storage-service-net: DB→localhost, MinIO→localhost:9000 (removed Velik@2026),
  Redis→localhost

All four files now point exclusively to local Docker Compose services
(postgres-local:5432, redis-local:6379, minio-local:9000).
Production/staging credentials must be injected via environment variables.

CTO review finding: appsettings.Development.json must not contain cloud
credentials. Local dev should always use docker-compose services.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 09:52:20 +07:00
Ho Ngoc Hai
af0461f233 fix(frontend): resolve 4 P2 architecture issues (Wave 3)
FRONT-I-01: Extract Auth components to Razor Class Library packages/blazor-ui/
- Created GoodGo.BlazorUi RCL (net10.0, MudBlazor 8.15) at packages/blazor-ui/
- Moved AuthButton, AuthCard, AuthInput, OtpInput, BrandPanel, SocialLogin, LanguageSwitcher
- Referenced RCL from WebClientTpos.Client via ProjectReference
- Added GoodGo.BlazorUi.Components.Auth/Common namespaces to _Imports.razor

FRONT-I-02: Add ARIA/accessibility attributes (WCAG 2.1 AA)
- AuthButton: aria-label, aria-busy, aria-disabled, aria-hidden on decorative icons
- OtpInput: role=group, aria-label per digit, autocomplete=one-time-code
- PosLayout: aria-expanded + aria-controls on sidebar/order toggles, aria-label on all icon buttons

FRONT-I-03: Implement Style Dictionary design token pipeline
- Created packages/design-tokens/ with token JSON (color, spacing, typography, border)
- Style Dictionary config outputs: CSS custom properties → wwwroot/css/tokens.generated.css
- Second output: C# constants → packages/blazor-ui/DesignTokens/DesignTokens.g.cs
- Added tokens:build script to root package.json
- Added tokens.generated.css link to index.html (before app.css for cascade correctness)

FRONT-I-04: Replace eval() in OtpInput with safe JS interop
- Created wwwroot/js/otp-input.js with window.focusOtpInput(index) helper
- Replaced JS.InvokeVoidAsync("eval", ...) with JS.InvokeVoidAsync("focusOtpInput", index)
- Eliminates CSP-violating eval(), improves security and debuggability

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 09:50:13 +07:00
Ho Ngoc Hai
97b54ebd39 fix(security): fix 5 P1 backend issues — BACK-C-01/03/04, BACK-W-02
BACK-W-02: Replace string-interpolated SET LOCAL SQL with parameterized
set_config() calls in TenantMiddleware across 5 services (order, wallet,
inventory, catalog, fnb-engine). Eliminates SQL injection pattern;
set_config(key, $1, true) is local-to-transaction, same semantics as SET LOCAL.

BACK-C-01: Remove AllowAnyOrigin() from all 26 services. Switch to
WithOrigins() reading AllowedOrigins config array, with dev-only fallback
to localhost. In production, set AllowedOrigins=["https://goodgo.vn",
"https://admin.goodgo.vn"] via environment config.

BACK-C-03: Standardize OrdersController GET /orders/{id} 404 response
from {Message:...} to {success:false, error:{code,message}} per API contract.

BACK-C-04: Add complete ProblemDetails exception mappings to _template_dot_net:
ValidationException -> 400, DomainException -> 422, with TODO comments
for service-specific types (EntityNotFoundException -> 404, etc.).

BACK-C-02: wallet-service and booking-service already have full
IRequestManager idempotency implementation — no changes needed.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 09:48:22 +07:00
Ho Ngoc Hai
25f68781ad fix(security): fix 5 P0 security blockers — SEC-C-01 through SEC-C-05
SEC-C-01: Replace Neon PostgreSQL credentials (npg_Ssfy6HKO0cXI) with local
dev connection strings in all 19 appsettings.json files. Production credentials
must be injected via ConnectionStrings__DefaultConnection env var. Add
appsettings.Production.json and appsettings.Staging.json to .gitignore.

SEC-C-02: Add services/goodgo-mcp-server/.env to root .gitignore. Create
.env.example with safe placeholder values documenting required variables.

SEC-C-03: Wrap AddDeveloperSigningCredential() in env check — development only.
Non-development environments must provide X.509 certificate via
IdentityServer:SigningCertificatePath and IdentityServer:SigningCertificatePassword.

SEC-C-04: Remove 4 unauthenticated debug endpoints from StaffController:
GET debug/all, POST debug/seed, POST debug/update-userid, POST debug/update-merchant.
These endpoints allowed privilege escalation and data exfiltration without auth.

SEC-C-05: Removed endpoints containing SQL injection via string interpolation
(lines 307, 367 in StaffController). Also removed [AllowAnonymous] from
GET lookup endpoint — inherits class-level [Authorize].

BREAKING: debug/* endpoints are permanently removed. BFF lookup endpoint now
requires authentication.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 09:47:07 +07:00
Ho Ngoc Hai
6d0ca5bee5 fix: resolve 4 P0 DevOps blockers — image tags, alertmanager, port conflicts
DEVOPS-C-01: Replace hardcoded :latest with IMAGE_TAG placeholder in all 8
production K8s manifests. Update deploy-production.yml to sed-replace
IMAGE_TAG with commit SHA before kubectl apply (remove now-redundant
kubectl set image step).

DEVOPS-C-02: Configure Alertmanager — create alertmanager.yml with Slack +
email receivers (critical/warning/infra routes, inhibition rules). Add
alertmanager:v0.27.0 service to both docker-compose.observability.yml and
deployments/local/docker-compose.yml. Enable prometheus.yml target
(alertmanager:9093).

DEVOPS-C-03: Remove :latest from docker-build.yml main branch push. Now
only SHA tag is pushed for main; :staging+SHA for develop.

DEVOPS-C-04: Add 4 mkt-* services to deployments/local/docker-compose.yml
with unique host ports (facebook:5021, whatsapp:5022, x:5023, zalo:5024)
to eliminate port 5000 conflicts. Add corresponding Traefik routers and
load-balancer entries in infra/traefik/dynamic/routes.yml
(/api/v1/mkt/{facebook,whatsapp,x,zalo}).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 09:46:32 +07:00
Ho Ngoc Hai
cdc67d768f docs: fix Wave 3 documentation gaps — remove hardcoded credentials and add marketing theme ADR
- DOC-W-01: Replace hardcoded test account passwords/emails in ROADMAP.md Section IX
  with .env.local variable references and seed script pointer (security hygiene)
- DOC-W-02: Create docs/adr/001-marketing-dual-theme.md documenting why MarketingLayout
  uses MarketingDark (#FACC15 yellow) instead of DefaultDark (#FF5C00 orange),
  including contrast rationale, alternatives considered, and implementation reference

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 09:45:10 +07:00
Ho Ngoc Hai
d0211e5a3c docs: full documentation audit — update 7 files, create MCP SERVICE_DOCS
Project-level docs:
- README.md: rewrite with correct tech stack (.NET 10/Blazor, not Node.js/Flutter)
- ROADMAP.md: add MCP server, shop lifecycle, onboarding redesign, POS nav fix
- CLAUDE.md: add goodgo-mcp-server to project structure
- CTO_REPORT_SHOP_DELETE.md: status OPEN → RESOLVED (implemented in 6263eeb)

MCP Server docs:
- SERVICE_DOCS.md: new file — 12 tools reference, architecture, setup guide

Frontend docs:
- web-client-tpos-net README: fix wrong paths (web-client-base-net → web-client-tpos-net)
- web-client-tpos-net docs/en/README: same path fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:46:15 +07:00
Ho Ngoc Hai
ca022de832 refactor: redesign onboarding wizard UI — inline step progress, improved layout
Redesign all 6 onboarding steps with inline step indicators replacing
the fixed sidebar layout. Simplified structure with admin-content/admin-panel
pattern for consistency with other admin pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:42:10 +07:00
Ho Ngoc Hai
3c43ca519e fix: MCP server full audit — fix 4 critical + 8 high severity issues
CRITICAL fixes:
- update_product: fetch current product first, include productId in body (was 400)
- period enum: "week"/"month" → "7d"/"30d" to match backend handler
- Token leakage: add axios response interceptor to strip Authorization from errors
- Token expiry: add 401 detection with clear user-facing message

HIGH fixes:
- create_product: handle raw Guid response (was returning "unknown")
- update_product: merge with existing values to avoid overwriting with defaults
- Startup validation: warn if API_TOKEN is not set
- Graceful shutdown: handle SIGINT/SIGTERM with server.close()
- Error handler: shared module with structured API error extraction
- Type safety: replace `any` with proper DTO interfaces across all tools
- Promise.allSettled in cost_analysis for partial failure resilience
- Timeout increased 15s → 30s for analytics queries

MEDIUM fixes:
- amount fields use .int() (inventory backend expects int, not float)
- ingredients array requires .min(1) (prevent empty recipes)
- isActive default removed (show all products by default)
- pageSize default aligned to 20 (matches backend)
- String length limits on name/description fields
- Locale-explicit formatting (vi-VN)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 14:14:49 +07:00
Ho Ngoc Hai
20cf8781b8 fix: MCP server audit — correct API routing through Traefik gateway
CTO Audit findings and fixes:
- Port config: all 4 services were using wrong localhost ports (5002/5003/5004/5019).
  All services run behind Traefik on port 80 — consolidated to single gateway client.
- Route fix: /shops/{shopId}/products → /products?shopId= (Traefik routes /shops to merchant-service)
- Response parsing: dashboard API uses "revenue"/"popularItems" (not "totalRevenue"/"topItems")
- Added .gitignore to prevent .env with JWT tokens from being committed

Verified all 12 tools against live Docker services via Traefik gateway.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 17:03:18 +07:00
Ho Ngoc Hai
b7a194f14b feat: add GoodGo MCP server for AI-assisted F&B operations
MCP server with 12 tools across 4 groups:
- Catalog: list/create/update/delete products
- Inventory: check stock, record intake/usage, low stock alerts
- Recipes: list and create recipes with ingredients
- Analytics: popular items, cost analysis

Uses @modelcontextprotocol/sdk with stdio transport for Claude Code integration.
Connects to catalog-service, inventory-service, fnb-engine via REST APIs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 12:55:58 +07:00
Ho Ngoc Hai
6263eeb05d feat: add shop lifecycle management UI — deactivate & close shop
- Add "Danger Zone" section to ShopSettings with deactivate/close actions
- CloseShopConfirmDialog: type shop name to confirm (GitHub-style)
- BFF: proxy endpoints POST /shops/{id}/deactivate and /close
- MerchantApiService: DeactivateShopAsync(), CloseShopAsync()
- CTO report documenting the gap and implementation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 07:35:30 +07:00
Ho Ngoc Hai
659e8e05e5 fix: POS settings button navigates by role — staff→/staff, admin→/admin
PosLayout.razor hardcoded navigation to /admin for the settings button
and sidebar link, causing staff users to land on the admin page.
Now uses AuthStateService.GetPortalUrl() for role-aware routing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 07:24:08 +07:00
Ho Ngoc Hai
b40662fbd8 docs: pure staff session POS tests — 38/41 pass
Added T39-T41: Staff POS tested with only aPOS_token_staff
(admin token removed). Bank transfer 120k, dashboard 358k,
order history all verified in staff-only context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 07:17:17 +07:00
Ho Ngoc Hai
eda45e11a3 docs: Staff POS E2E complete — 35/38 pass, full order flow verified
Added T35-T38: Staff POS order creation (238k cash), pending orders
(6 orders with status filters), POS dashboard (real revenue), and
order history (4 orders across 7 days). All Staff POS features working.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 07:06:37 +07:00
Ho Ngoc Hai
a1e36f1266 docs: POS E2E test results — 31/34 pass, cash + bank transfer verified
Added T28-T34: full POS order flow tests (create order, cash payment
with change, bank transfer, order history, dashboard, finance, reports).
Updated CTO report with P0 bug fixes and deployment readiness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 22:03:55 +07:00
Ho Ngoc Hai
cb5bc95b8d fix: remove [Authorize] from BFF OrderController — BFF proxies don't configure auth schemes
BFF server forwards JWT via AuthForwardingHandler to downstream services.
Adding [Authorize] on BFF controllers causes "No authenticationScheme was specified"
error since the BFF server itself has no JWT middleware configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 22:02:24 +07:00
Ho Ngoc Hai
dd74b05189 docs: CTO report — both bugs verified fixed on Chrome UI
P2: POS duplicates were DB seed issue (9 records deleted), now 9 unique products.
P3: Settings shop name now shows "Cobic Coffee" correctly.
Both verified visually on Chrome.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:31:09 +07:00
Ho Ngoc Hai
db72640198 docs: update CTO deployment report — bugs fixed, ready for staging
Both P2 (duplicate POS products) and P3 (settings shop name) resolved
in commit 344be33. Updated checklist and next steps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:27:22 +07:00
Ho Ngoc Hai
344be332d7 fix: resolve POS duplicate products + settings shop name display
P2: Products appeared 2x in POS grid — BFF now filters isActive=true
by default, plus client-side dedup by product ID as safety net.
P3: Admin Settings showed "--" for shop name — parent ShopPage now
passes ShopName and VerticalLabel parameters to ShopSettings component.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:26:08 +07:00
Ho Ngoc Hai
5e666010b6 docs: QA complete — 24/27 pass, CTO deployment report created
27 test cases covering all admin (15 pages) and staff (7 pages) features.
Zero regressions from Wave 1-3 fixes. 2 bugs found: duplicate POS products (P2),
settings shop name display (P3). Recommendation: ready for staging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:18:03 +07:00
Ho Ngoc Hai
ea1608cf94 docs: TESTER_TRACKER — 11/14 tests passed on Chrome UI
Regression test after Wave 1-3 fixes (202 files changed):
- T1-T8: All core flows PASS (admin login, staff login, dashboard,
  schedule, attendance, leave)
- T10-T11: Auth token + multi-role sessions PASS
- T13: Empty states PASS
- No regressions found from code changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:46:18 +07:00
Ho Ngoc Hai
4f8a205af0 docs: CTO_FIX_TRACKER — all 3 waves complete
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:34:31 +07:00
Ho Ngoc Hai
efabe49157 refactor(P2): standardize API responses + fix migrations + cleanup DI
Wave 3 — 3 parallel agents fixing P2 code quality issues:

Response format standardization (30 controllers across 8 services):
- Wrapped all raw DTO returns with { success: true, data: result }
- Standardized error responses with { success: false, error: { code, message } }
- Services: chat, social, membership, ads-manager, ads-serving,
  ads-billing, ads-tracking, ads-analytics
- booking-service already compliant (skipped)

Migration fixes:
- ads-billing: Fixed InvoiceId1 spurious FK (explicit HasMany navigation)
- Removed unused IRequestManager DI from: ads-analytics, ads-serving,
  booking, mkt-facebook (classes preserved for future use)

Unused dependencies:
- No Redis/Dapper DI registrations found (only NuGet refs, kept as-is)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:34:10 +07:00
Ho Ngoc Hai
52c818ea83 docs: update CTO_FIX_TRACKER — Wave 1+2 complete (P0+P1 resolved)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:24:30 +07:00
Ho Ngoc Hai
59b2cecaf2 feat(P1): add 57 validators + 10 missing handlers across 13 services
Wave 2 — 3 parallel agents fixing P1 issues:

Validators (57 new FluentValidation validators):
- ads-manager: 10 validators for all commands
- ads-billing: 3 validators for all commands
- ads-tracking: 2 validators for missing commands
- ads-analytics: 1 validator for CreateReport
- social: 8 validators for all commands
- mining: 16 validators for all commands
- mission: 4 validators for all commands
- promotion: 13 validators for all commands

Missing handlers (10 implemented):
- promotion: ExchangeVoucher, PurchaseVoucher, SearchVouchers,
  GetCampaignStatistics, GetCampaignVouchers
- mission: GetUserMissionProgress
- mkt-facebook: GetConversations, GetCustomers
- ads-manager: ListAudiences, GetAudienceById

All validators use bilingual messages (EN/VI) and are auto-registered
via MediatR ValidatorBehavior pipeline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:24:06 +07:00
Ho Ngoc Hai
f8606e0447 fix(P0): security hardening + critical bug fixes across 22 services
Wave 1 — 6 parallel agents fixing P0 issues from code audit:

Auth (18 services secured):
- Added JWT Bearer auth + [Authorize] to all unprotected controllers
- Webhook endpoints (Facebook/WhatsApp/Zalo/X) stay [AllowAnonymous]
- Health checks remain public for Docker/K8s probes
- Services: catalog, order, booking, fnb-engine, inventory, social,
  ads-manager, ads-serving, ads-billing, ads-tracking, ads-analytics,
  mkt-facebook, mkt-whatsapp, mkt-x, mkt-zalo, promotion

Template artifacts (4 services):
- mission-service: myservice_db → mission_service
- mkt-facebook: Dockerfile MyService.API → FacebookService.API
- mkt-whatsapp: MyServiceContext.cs → WhatsAppServiceContext.cs
- promotion: UserSecretsId fixed

Critical handler bugs (7 fixes):
- ads-tracking: TrackPixelEventHandler now persists to DB
- ads-tracking: RecordConversion endpoint exposed via controller
- booking: UpdateResource now applies Name + Capacity changes
- ads-manager: ListPendingAds uses correct enum (pending_review)
- mining: BanMiner calls Ban() not Suspend()
- mining: ResetMinerStreak now actually resets streak
- mkt-x: 8 missing repository DI registrations added

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:18:09 +07:00
Ho Ngoc Hai
f3779c4ebe docs: add SERVICE_DOCS.md for all 24 microservices from per-service code audit
Each SERVICE_DOCS.md documents: Overview, API Endpoints, Commands, Queries,
Domain Model, Database Schema, Integration Events, Dependencies, Configuration.
Generated by 23 parallel audit agents reading actual source code.

Key corrections from audit:
- inventory-service: 12 commands/6 queries (was listed as scaffold)
- promotion-service: 12 commands/10 queries (was listed as 0)
- mission-service: 4 commands/7 queries (was listed as 0)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:54:53 +07:00
Ho Ngoc Hai
afe8b7c966 docs: comprehensive ROADMAP rewrite from full platform audit
- 4 parallel agents audited: backend (23 services), frontend (5 apps),
  infrastructure (Docker/K8s/CI/CD), database schemas
- Corrected service status: 15 production-ready, 5 in-progress, 3 scaffold
  (was inaccurately showing 8 production-ready)
- Added deployment matrix: Docker/Traefik/K8s/CI coverage per service
- Identified critical gaps: inventory (1/12 handlers), promotion (0),
  mission (0), 13 missing Traefik routes, 4 mkt-* not in Docker
- Updated POS vertical status with actual page counts (141 total)
- Added database architecture section (multi-tenant patterns)
- Added environment/test accounts with verified IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:38:46 +07:00
Ho Ngoc Hai
fffedea785 fix: schedule pages — real API data, role display, time formatting
- Rewrite StaffSchedule.razor from hardcoded stub to real API integration
  (profile → shop schedules → filter by staffId)
- Fix admin ShopSchedule role column: use staff role from merchant data
  instead of showing "—"
- Add FormatTime() helper to strip seconds from time display (08:00:00 → 08:00)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:06:40 +07:00
Ho Ngoc Hai
deffb9de4a fix: resolve attendance staffName display and token conflict between staff/admin sessions
1. Attendance API now joins with MerchantStaff to return staffName instead of showing truncated staffId
2. AuthService uses role-suffixed localStorage keys (aPOS_token_owner, aPOS_token_staff) to prevent
   staff and admin tokens from overwriting each other on the same origin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 16:38:01 +07:00
Ho Ngoc Hai
ddd02be26e feat: Display attendance times in local timezone and calculate staff leave statistics from real data, removing stub notifications. 2026-03-13 16:23:00 +07:00
Ho Ngoc Hai
598193e6cb fix: wire up admin attendance page to real API, add BFF shop attendance endpoint
- BFF: add GET /api/bff/shops/{shopId}/attendance proxy to merchant-service
- ShopAttendance.razor: replace mock data with real attendance API call
- Calculate present/late/absent counts from actual attendance records

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:30:12 +07:00
Ho Ngoc Hai
8086bc627f fix: resolve HR module bugs — leave approval, staff auth timing, EF Core mapping
- BFF: extract approver/rejector userId from JWT instead of accepting Guid.Empty from client
- Staff pages (Dashboard, Leave, Attendance): move data loading to OnAfterRenderAsync
  to fix token timing bug where OnInitializedAsync runs before auth session is restored
- EF Core: fix AttendanceRepository to use public properties after HasField() migration
- LeaveRequest: fix DateTime UTC kind for Npgsql 10 compatibility
- merchant-service: add debug seed endpoints for staff/shop test data
- EF configs: migrate to HasField() pattern for private field mapping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:23:35 +07:00
Ho Ngoc Hai
aba5ee1162 fix: resolve inventory display bugs — transaction history & item type mapping
- Fix DTO field mismatch: QuantityChange→Quantity, Reason→Notes in PosDataService
- Fix ItemType enum mismatch: FinishedProduct→FinishedGood, Supply→Consumable in ShopInventory
- Add ResolveTransactionTypeName fallback in InventoryMapper when Type nav property is null
- Add "In"/"Out" alternative matches for TransactionType in history display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:54:44 +07:00
Ho Ngoc Hai
e4e7cca1ce fix: resolve Blazor compilation errors — RZ9986, RZ10010, CS0246
- StaffPerformance.razor: fix mixed C#/markup in Style attributes (RZ9986) using @($"...")
- AppointmentCalendar.razor: fix duplicate DateChanged parameter (RZ10010) using @bind-Date:after
- RetailDesktop.razor: add missing @using WebClientTpos.Client.Services (CS0246)
- ReturnDialog.razor: add missing @using WebClientTpos.Client.Services (CS0246)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 01:26:25 +07:00
Ho Ngoc Hai
5c3d90ee44 docs: update ROADMAP — Phase 2 close-out complete, all 5 verticals working
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:59:05 +07:00
Ho Ngoc Hai
76b5e6afd0 feat: Phase 2 close-out — multi-branch management, production K8s, revenue dashboard UI, responsive POS
Backend:
- Multi-branch shop management: SetDefaultShop, TransferShop commands, GetMerchantShops paginated query
- Shop aggregate: IsDefault field, SetAsDefault/ClearDefault/TransferOwnership behavior methods
- 2 new domain events: ShopSetAsDefaultDomainEvent, ShopTransferredDomainEvent

Frontend:
- Revenue Dashboard (MudChart line/donut/bar, 4 KPI cards, top products table)
- Staff Performance (sortable table, color-coded completion rates, CSV export)
- Customer QR Menu page (/menu/{ShopId}, mobile-first, Vietnamese labels)
- QR Code Generator admin page (batch generate, print-all, per-table QR)
- Responsive POS layout (collapsible sidebar, slide-out order drawer, touch-friendly CSS)
- ResponsiveOrderPanel component (desktop inline / tablet drawer / mobile overlay)

Infrastructure:
- Production K8s manifests: 8 services (3 replicas, 512Mi-1Gi, HPA min3/max10), Redis with persistence
- Production ingress: api.goodgo.vn, cert-manager TLS, rate-limit middleware
- Deploy script: pre-flight checks, dry-run, single-service deploy, rollback support
- CI/CD: deploy-production.yml with environment approval, commit SHA tags
- Prometheus full scrape config (11 targets), docker-compose observability stack
- Production deployment checklist (80+ items)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:58:40 +07:00
Ho Ngoc Hai
a6ea9fa29b docs: update ROADMAP — Phase 2 W7-8 complete (analytics, QR menu, E2E tests, observability)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:52:20 +07:00
Ho Ngoc Hai
dc1ea7c0d2 feat: Phase 2 W7-8 production readiness — QR menu, analytics, E2E tests, observability
- Public QR menu: BFF proxy endpoints (no auth), PosDataService public methods
- Revenue analytics + staff performance: Dapper queries, validators, BFF proxy
- Playwright E2E tests: 8 spec files covering auth, admin, 5 POS verticals, reports
- Observability: Grafana dashboard (HTTP metrics, infra, business), Prometheus alert rules
- Fixes: validator frozen-date bug (Must vs LessThanOrEqualTo), PublicMenuController logging + CancellationToken

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:51:37 +07:00
Ho Ngoc Hai
870f1218f8 feat: Phase 2 frontend — Spa, Retail, Cafe Blazor UI pages and BFF proxies
Spa/Beauty UI (booking-service integration):
- TherapistManagement.razor: CRUD table, specialty multi-select, working hours
- AppointmentCalendar.razor: daily calendar grouped by therapist, color-coded statuses
- ShopTherapists embedded component for ShopPage, sidebar menu for spa/beauty
- BookingController BFF: therapist CRUD + appointment proxy endpoints
- Localization: vi-VN + en-US for "Nhân viên trị liệu"

Retail POS UI (catalog + inventory + order integration):
- RetailDesktop.razor: barcode input, API lookup, stock badges, cart warnings
- ReturnDialog.razor: order lookup, return/exchange mode toggle, refund summary
- StockOverview.razor: admin stock table, search/filter, threshold edit dialog
- PosDataService: barcode lookup, bulk stock, return/exchange API methods

Cafe UI (membership + fnb-engine integration):
- StampCard.razor: visual stamp grid, animated fill, celebration UI, claim/reset
- BaristaQueue.razor: 3-column Kanban, stats bar, auto-refresh 10s, pulse animation
- CafeController BFF: stamp cards + barista queue proxy endpoints

Infrastructure:
- Traefik: added /api/v1/therapists + /api/v1/appointments to booking-service
- ROADMAP: Phase 2 vertical tasks DONE, UI refinement IN-PROGRESS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 17:03:55 +07:00
Ho Ngoc Hai
0d03feeffd feat: Phase 2 multi-vertical expansion — Spa appointments, Retail POS, Cafe loyalty
Spa/Beauty (booking-service) — Therapist + Appointment scheduling:
- Therapist aggregate: specialties (text[]), workingHours (jsonb), CRUD
- Appointment: notes field, Pending initial status, MarkNoShow() behavior
- TherapistsController (4 endpoints), 9 FluentValidation validators
- EF config: PostgreSQL native text[] + jsonb column types

Retail POS (catalog + inventory + order) — Barcode, stock, returns:
- Product: barcode/SKU fields, GetProductByBarcodeQuery (lookup endpoint)
- Inventory: bulk stock check, low stock alert threshold (SetReorderLevel)
- Order: return/exchange flow with ProcessReturn(), Returned status (id=8)
- CreateReturnCommand, CreateExchangeCommand (same UnitOfWork)
- 2 domain events: OrderReturnedDomainEvent, OrderExchangedDomainEvent
- 6 new API endpoints across 3 services

Cafe (membership + fnb-engine) — Loyalty stamps + barista queue:
- StampCard aggregate: AddStamp(), ClaimReward(), Reset(), 4 domain events
- Auto-create card on first stamp (friction-free UX)
- StampCardsController (6 endpoints), 4 commands, 2 queries
- BaristaQueueItem: 5-status workflow (Queued→Preparing→Ready→Delivered)
- BaristaController (6 endpoints), 5 commands, 2 queries
- Tenant isolation (shop-level) on both features

ROADMAP: Phase 1 closed out, Phase 2 vertical tasks IN-PROGRESS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:45:43 +07:00
Ho Ngoc Hai
a7a753bf38 feat: EOD reports, security audit (rate limiting + 44 validators), and 30 critical path tests
EOD Reports & Daily Close (order-service + Blazor UI):
- GetEodReportQuery: Dapper query for revenue, orders, payment breakdown, top items, hourly chart
- CloseDayCommand: check pending orders, generate final report
- EodReport.razor: 6 KPI cards, donut/bar charts, top 10 table, close-day dialog
- FluentValidation for both query and command
- BFF proxy endpoints for reports

Security Audit — Rate Limiting:
- Tighten auth-ratelimit from 100 to 10 req/min (brute force protection)
- Add payment-ratelimit (30/min), api-ratelimit (100/min), hub-ratelimit (500/min)
- Apply rate limits to ALL Traefik routers (previously many had none)

Security Audit — Input Sanitization (44 missing validators created):
- iam-service: 14 validators (auth, user, role commands)
- merchant-service: 11 validators (admin, attendance commands)
- wallet-service: 7 validators (wallet, points commands)
- fnb-engine: 7 validators (session, table, ticket, reservation)
- catalog-service: 6 validators (product, category CRUD)
- storage-service: 6 validators (upload, share, quota)
- order-service: 2 validators (complete order/payment)

Critical Path Unit Tests (30 new tests):
- inventory-service: 12 tests (deduction, partial stock, idempotency)
- wallet-service: 14 tests (create payment, process callback, domain events)
- fnb-engine: 8 tests (kitchen-served event handler, inventory client integration)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:33:39 +07:00
Ho Ngoc Hai
653322b26c fix: resolve 12 critical/high issues from code review across backend, frontend, and infra
Backend (7 fixes):
- wallet-service: remove conflicting EF Ignore() calls for mapped backing fields
- fnb-engine: remove KitchenTicket short constructor that set productId=orderItemId
- fnb-engine: replace fire-and-forget Task.Run with direct await for inventory deduction
- TenantMiddleware: implement PostgreSQL RLS SET LOCAL in 4 services (wallet, fnb, inventory, catalog)
- order-service: fix SQL injection pattern in TenantMiddleware with Guid.ToString("D")
- order-service: add ValidateShopAccess() authorization check in SignalR PosHub
- 4 services: register IDbConnection (NpgsqlConnection) in DI for RLS middleware

Frontend (3 fixes):
- PosDataService: return Success=false (not true) when PayOrder response parsing fails
- QrPayment: add _disposed guard to prevent timer race condition after component disposal
- BFF OrderController: add [Authorize] attribute to require JWT for all endpoints

Infrastructure (3 fixes):
- docker-compose: upgrade PostgreSQL 15-alpine to 16-alpine per project spec
- init-databases.sh: add 4 missing marketing service databases (mkt_*)
- Traefik routes: add wallet, catalog, booking routers and /api/v1/stock path

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:22:08 +07:00