Skip to content

feat: Implement product service with gateway auth fixes - #4

Merged
rezadrian01 merged 6 commits into
mainfrom
feature/product-service
May 28, 2026
Merged

feat: Implement product service with gateway auth fixes#4
rezadrian01 merged 6 commits into
mainfrom
feature/product-service

Conversation

@rezadrian01

Copy link
Copy Markdown
Owner

Summary

  • Fix JWT algorithm mismatch — API Gateway was validating tokens with RSA/RS256 while user-service signs with HS256. Replaced the RSA middleware with HS256 using the shared JWT_SECRET, and fixed claim extraction (sub field for user UUID).
  • Wire gateway auth middlewareRequireAuth and RequireRole("admin") were defined but never applied. All protected routes (users, cart, orders, payments GET, inventory, product/category writes) now enforce authentication; public GETs and the Stripe webhook remain open.
  • Implement product service — Domain, repository, cache, service, handler, route, and bootstrap layers are all complete and wired. Features: full CRUD, PostgreSQL full-text search via tsvector, Redis cache-aside (5 min TTL), list cache invalidation on writes, Kafka event publishing (product.created/updated/deleted), float64 price type compatible with GORM.
  • Fix GenerateCacheKey nil panicCategoryID, MinPrice, and MaxPrice pointer fields are now guarded before dereference.
  • Fix docker-compose — Added REDIS_URL and KAFKA_BROKERS to product-service (missing caused startup crash); added JWT_SECRET to api-gateway.
  • Remove broken BeforeSave GORM hook — Replaced by the PostgreSQL BEFORE INSERT/UPDATE trigger installed at migration time.

Commits

SHA Description
b4faafa Domain layer — entities, interfaces, errors, events
9d98d84 Repository — CRUD, FTS, filtering, sorting, pagination
e05aae1 Cache — Redis implementation, list invalidation
c49eda0 Fix JWT algorithm + wire gateway auth middleware
3a0c50d Service layer, handler, route, cmd bootstrap, Dockerfile
78508fb Fix runtime blockers (float64 price, docker-compose env vars, cleanup)

Test plan

  • go build ./... passes in services/api-gateway and services/product-service
  • POST /api/auth/login returns JWT; token accepted by GET /api/users/me
  • GET /api/products without token → 200
  • POST /api/products without token → 401
  • POST /api/products with admin token → 201
  • GET /api/products?q=<term> returns full-text search results
  • GET /api/products?min_price=10 does not panic (nil pointer fix)
  • docker compose up starts product-service without crashing (Redis + Kafka env vars present)
  • Stripe webhook POST /api/payments/webhook/stripe without token → proxied (not 401)

🤖 Generated with Claude Code

rezadrian01 and others added 6 commits April 15, 2026 09:25
- Replace RSA/RS256 JWT validation in api-gateway with HS256 HMAC to
  match tokens issued by user-service; token claims now read user UUID
  from the standard "sub" field instead of the missing "user_id" field
- Add JWT_SECRET env var to api-gateway config, docker-compose, and
  .env files; remove defunct JWT_PUBLIC_KEY / PEM file loading
- Wire RequireAuth and RequireRole("admin") into all protected routes:
  logout, /users/*, /cart/*, /orders/*, payments GET, inventory (admin),
  and product/category write endpoints (admin); public GETs unchanged
- Stripe webhook route kept unauthenticated (Stripe signs its own payload)
- Add FIXES_PLAN.md with implementation plan for remaining tasks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task 3 — Service layer:
- Replace all stub methods in product_service.go with real implementations
  matching the domain.ProductService interface (context.Context + uuid.UUID)
- Cache-aside pattern for GetProductByID and GetProducts (Redis → DB fallback)
- Write methods (Create/Update/Delete) invalidate list cache and evict detail
  cache; Kafka events published async in goroutine so they never block HTTP
- CreateProduct and UpdateProduct verify the target category exists first
- CreateCategory guards slug uniqueness before inserting
- normalizeFilter sets page/limit defaults and validates sort param
- buildListCacheKey is nil-safe for CategoryID, MinPrice, MaxPrice pointers

Task 4 — Entry point:
- Add main.go, cmd/{config,dotenv,infrastructure,kafka,run,server}.go
- Wire dependency graph: DB → repo, Redis → cache, Kafka → publisher → service → handler
- infrastructure.go runs AutoMigrate for Category/Product/Inventory then applies
  the tsvector trigger SQL from db/004_create_search_index.up.sql via db.Exec
- kafka.go creates one kafka.Writer per product topic; ensureTopics on startup
- Graceful shutdown closes DB, Redis, and Kafka writers on SIGINT/SIGTERM
- Add internal/events/kafka_publisher.go (mirrors user-service pattern)
- Add internal/handler/product_handler.go with productBody DTO using float64
  for price (clean JSON API) converted to genproto Decimal on service call
- Add internal/route/product_route.go registering all product + category routes
- Add Dockerfile (multi-stage golang:1.25-alpine → alpine:3.18, port 8082)
- Add .env.example; pull in gin, gorm/driver/postgres, kafka-go dependencies

Task 5 — Fix GenerateCacheKey nil panic:
- Guard filter.CategoryID, MinPrice, MaxPrice pointer dereferences
- Fix InvalidateProductList scan pattern to "product:list:*" matching new key format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace genproto decimal.Decimal with float64 for Product.Price,
  ProductRequest.Price, and ProductResponse.Price — genproto's Decimal
  does not implement sql.Scanner/driver.Valuer so GORM could not read
  or write price values from PostgreSQL at runtime
- Remove BeforeSave GORM hook and buildSearchVector helper from Product;
  the PostgreSQL trigger installed by applySearchIndex handles tsvector
  population correctly — the Go-side hook was redundant and wrote plain
  text into a tsvector column which risks type errors on some PG versions
- Remove genproto decimal import from handler; simplify toDomain() to
  assign b.Price (float64) directly now that domain type matches
- Add REDIS_URL and KAFKA_BROKERS to product-service in docker-compose.yml;
  without REDIS_URL the service panicked at startup before serving requests
- Remove stale commented-out duplicate import in product_repository.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 28, 2026 03:47
@rezadrian01
rezadrian01 merged commit 5c6a55f into main May 28, 2026
1 of 2 checks passed
@rezadrian01
rezadrian01 removed the request for review from Copilot May 28, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant