style: auto-format code via Prettier [skip ci]
This commit is contained in:
@@ -56,7 +56,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -90,7 +90,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -114,7 +114,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -138,7 +138,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -161,7 +161,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -189,7 +189,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -211,7 +211,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -234,7 +234,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -259,7 +259,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -284,7 +284,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -307,7 +307,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -330,7 +330,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -355,7 +355,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -379,7 +379,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -425,7 +425,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -448,7 +448,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -476,7 +476,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -502,7 +502,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -529,7 +529,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -555,7 +555,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -579,7 +579,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -612,7 +612,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -637,7 +637,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -656,7 +656,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -681,7 +681,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -705,7 +705,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -757,7 +757,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Measurements**: **********************\_\_\_**********************
|
||||
**Measurements**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -765,7 +765,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
### Test 8.1: Chrome/Edge
|
||||
|
||||
**Browser Version**: ******\_\_\_******
|
||||
**Browser Version**: **\*\***\_\_\_**\*\***
|
||||
|
||||
**Tests to Run**:
|
||||
|
||||
@@ -775,13 +775,13 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
### Test 8.2: Firefox
|
||||
|
||||
**Browser Version**: ******\_\_\_******
|
||||
**Browser Version**: **\*\***\_\_\_**\*\***
|
||||
|
||||
**Tests to Run**:
|
||||
|
||||
@@ -791,13 +791,13 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
### Test 8.3: Safari (macOS/iOS)
|
||||
|
||||
**Browser Version**: ******\_\_\_******
|
||||
**Browser Version**: **\*\***\_\_\_**\*\***
|
||||
|
||||
**Tests to Run**:
|
||||
|
||||
@@ -807,7 +807,7 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
**Pass/Fail**: [ ]
|
||||
|
||||
**Notes**: **********************\_\_\_**********************
|
||||
**Notes**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
|
||||
---
|
||||
|
||||
@@ -849,8 +849,8 @@ podman exec -it flyer-crawler-dev npm run dev:container
|
||||
|
||||
## Sign-Off
|
||||
|
||||
**Tester Name**: **********************\_\_\_**********************
|
||||
**Date Completed**: **********************\_\_\_**********************
|
||||
**Tester Name**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
**Date Completed**: ****\*\*****\*\*****\*\*****\_\_\_****\*\*****\*\*****\*\*****
|
||||
**Overall Status**: [ ] PASS [ ] PASS WITH ISSUES [ ] FAIL
|
||||
|
||||
**Ready for Production**: [ ] YES [ ] NO [ ] WITH FIXES
|
||||
|
||||
@@ -208,7 +208,7 @@ Press F12 or Ctrl+Shift+I
|
||||
|
||||
**Result**: [ ] PASS [ ] FAIL
|
||||
|
||||
**Errors found**: ******************\_\_\_******************
|
||||
**Errors found**: **\*\*\*\***\*\***\*\*\*\***\_\_\_**\*\*\*\***\*\***\*\*\*\***
|
||||
|
||||
---
|
||||
|
||||
@@ -224,7 +224,7 @@ Check for:
|
||||
|
||||
**Result**: [ ] PASS [ ] FAIL
|
||||
|
||||
**Issues found**: ******************\_\_\_******************
|
||||
**Issues found**: **\*\*\*\***\*\***\*\*\*\***\_\_\_**\*\*\*\***\*\***\*\*\*\***
|
||||
|
||||
---
|
||||
|
||||
@@ -272,4 +272,4 @@ Check for:
|
||||
2. ***
|
||||
3. ***
|
||||
|
||||
**Sign-off**: ********\_\_\_******** **Date**: ****\_\_\_****
|
||||
**Sign-off**: **\*\*\*\***\_\_\_**\*\*\*\*** **Date**: \***\*\_\_\_\*\***
|
||||
|
||||
@@ -39,15 +39,15 @@ All cache operations are fail-safe - cache failures do not break the application
|
||||
|
||||
Different data types use different TTL values based on volatility:
|
||||
|
||||
| Data Type | TTL | Rationale |
|
||||
| ------------------- | --------- | -------------------------------------- |
|
||||
| Brands/Stores | 1 hour | Rarely changes, safe to cache longer |
|
||||
| Flyer lists | 5 minutes | Changes when new flyers are added |
|
||||
| Individual flyers | 10 minutes| Stable once created |
|
||||
| Flyer items | 10 minutes| Stable once created |
|
||||
| Statistics | 5 minutes | Can be slightly stale |
|
||||
| Frequent sales | 15 minutes| Aggregated data, updated periodically |
|
||||
| Categories | 1 hour | Rarely changes |
|
||||
| Data Type | TTL | Rationale |
|
||||
| ----------------- | ---------- | ------------------------------------- |
|
||||
| Brands/Stores | 1 hour | Rarely changes, safe to cache longer |
|
||||
| Flyer lists | 5 minutes | Changes when new flyers are added |
|
||||
| Individual flyers | 10 minutes | Stable once created |
|
||||
| Flyer items | 10 minutes | Stable once created |
|
||||
| Statistics | 5 minutes | Can be slightly stale |
|
||||
| Frequent sales | 15 minutes | Aggregated data, updated periodically |
|
||||
| Categories | 1 hour | Rarely changes |
|
||||
|
||||
### Cache Key Strategy
|
||||
|
||||
@@ -64,11 +64,11 @@ Cache keys follow a consistent prefix pattern for pattern-based invalidation:
|
||||
|
||||
The following repository methods implement server-side caching:
|
||||
|
||||
| Method | Cache Key Pattern | TTL |
|
||||
| ------ | ----------------- | --- |
|
||||
| `FlyerRepository.getAllBrands()` | `cache:brands` | 1 hour |
|
||||
| `FlyerRepository.getFlyers()` | `cache:flyers:{limit}:{offset}` | 5 minutes |
|
||||
| `FlyerRepository.getFlyerItems()` | `cache:flyer-items:{flyerId}` | 10 minutes |
|
||||
| Method | Cache Key Pattern | TTL |
|
||||
| --------------------------------- | ------------------------------- | ---------- |
|
||||
| `FlyerRepository.getAllBrands()` | `cache:brands` | 1 hour |
|
||||
| `FlyerRepository.getFlyers()` | `cache:flyers:{limit}:{offset}` | 5 minutes |
|
||||
| `FlyerRepository.getFlyerItems()` | `cache:flyer-items:{flyerId}` | 10 minutes |
|
||||
|
||||
### Cache Invalidation
|
||||
|
||||
@@ -86,14 +86,14 @@ The following repository methods implement server-side caching:
|
||||
|
||||
TanStack React Query provides client-side caching with configurable stale times:
|
||||
|
||||
| Query Type | Stale Time |
|
||||
| ----------------- | ----------- |
|
||||
| Categories | 1 hour |
|
||||
| Master Items | 10 minutes |
|
||||
| Flyer Items | 5 minutes |
|
||||
| Flyers | 2 minutes |
|
||||
| Shopping Lists | 1 minute |
|
||||
| Activity Log | 30 seconds |
|
||||
| Query Type | Stale Time |
|
||||
| -------------- | ---------- |
|
||||
| Categories | 1 hour |
|
||||
| Master Items | 10 minutes |
|
||||
| Flyer Items | 5 minutes |
|
||||
| Flyers | 2 minutes |
|
||||
| Shopping Lists | 1 minute |
|
||||
| Activity Log | 30 seconds |
|
||||
|
||||
### Multi-Layer Cache Architecture
|
||||
|
||||
|
||||
@@ -80,13 +80,13 @@ src/
|
||||
|
||||
**Common Utility Patterns**:
|
||||
|
||||
| Pattern | Classes |
|
||||
| ------- | ------- |
|
||||
| Card container | `bg-white dark:bg-gray-800 rounded-lg shadow-md p-6` |
|
||||
| Primary button | `bg-brand-primary hover:bg-brand-dark text-white rounded-lg px-4 py-2` |
|
||||
| Secondary button | `bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200` |
|
||||
| Input field | `border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2` |
|
||||
| Focus ring | `focus:outline-none focus:ring-2 focus:ring-brand-primary` |
|
||||
| Pattern | Classes |
|
||||
| ---------------- | ---------------------------------------------------------------------- |
|
||||
| Card container | `bg-white dark:bg-gray-800 rounded-lg shadow-md p-6` |
|
||||
| Primary button | `bg-brand-primary hover:bg-brand-dark text-white rounded-lg px-4 py-2` |
|
||||
| Secondary button | `bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200` |
|
||||
| Input field | `border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2` |
|
||||
| Focus ring | `focus:outline-none focus:ring-2 focus:ring-brand-primary` |
|
||||
|
||||
### Color System
|
||||
|
||||
@@ -187,13 +187,13 @@ export const CheckCircleIcon: React.FC<IconProps> = ({ title, ...props }) => (
|
||||
|
||||
**Context Providers** (see ADR-005):
|
||||
|
||||
| Provider | Purpose |
|
||||
| -------- | ------- |
|
||||
| `AuthProvider` | Authentication state |
|
||||
| `ModalProvider` | Modal open/close state |
|
||||
| `FlyersProvider` | Flyer data |
|
||||
| `MasterItemsProvider` | Grocery items |
|
||||
| `UserDataProvider` | User-specific data |
|
||||
| Provider | Purpose |
|
||||
| --------------------- | ---------------------- |
|
||||
| `AuthProvider` | Authentication state |
|
||||
| `ModalProvider` | Modal open/close state |
|
||||
| `FlyersProvider` | Flyer data |
|
||||
| `MasterItemsProvider` | Grocery items |
|
||||
| `UserDataProvider` | User-specific data |
|
||||
|
||||
**Provider Hierarchy** in `AppProviders.tsx`:
|
||||
|
||||
|
||||
@@ -45,15 +45,15 @@ Using **helmet v8.x** configured in `server.ts` as the first middleware after ap
|
||||
|
||||
**Security Headers Applied**:
|
||||
|
||||
| Header | Configuration | Purpose |
|
||||
| ------ | ------------- | ------- |
|
||||
| Content-Security-Policy | Custom directives | Prevents XSS, code injection |
|
||||
| Strict-Transport-Security | 1 year, includeSubDomains, preload | Forces HTTPS connections |
|
||||
| X-Content-Type-Options | nosniff | Prevents MIME type sniffing |
|
||||
| X-Frame-Options | DENY | Prevents clickjacking |
|
||||
| X-XSS-Protection | 0 (disabled) | Deprecated, CSP preferred |
|
||||
| Referrer-Policy | strict-origin-when-cross-origin | Controls referrer information |
|
||||
| Cross-Origin-Resource-Policy | cross-origin | Allows external resource loading |
|
||||
| Header | Configuration | Purpose |
|
||||
| ---------------------------- | ---------------------------------- | -------------------------------- |
|
||||
| Content-Security-Policy | Custom directives | Prevents XSS, code injection |
|
||||
| Strict-Transport-Security | 1 year, includeSubDomains, preload | Forces HTTPS connections |
|
||||
| X-Content-Type-Options | nosniff | Prevents MIME type sniffing |
|
||||
| X-Frame-Options | DENY | Prevents clickjacking |
|
||||
| X-XSS-Protection | 0 (disabled) | Deprecated, CSP preferred |
|
||||
| Referrer-Policy | strict-origin-when-cross-origin | Controls referrer information |
|
||||
| Cross-Origin-Resource-Policy | cross-origin | Allows external resource loading |
|
||||
|
||||
**Content Security Policy Directives**:
|
||||
|
||||
@@ -87,35 +87,35 @@ Using **express-rate-limit v8.2.1** with a centralized configuration in `src/con
|
||||
|
||||
```typescript
|
||||
const standardConfig = {
|
||||
standardHeaders: true, // Sends RateLimit-* headers
|
||||
standardHeaders: true, // Sends RateLimit-* headers
|
||||
legacyHeaders: false,
|
||||
skip: shouldSkipRateLimit, // Disabled in test environment
|
||||
skip: shouldSkipRateLimit, // Disabled in test environment
|
||||
};
|
||||
```
|
||||
|
||||
**Rate Limiters by Category**:
|
||||
|
||||
| Category | Limiter | Window | Max Requests |
|
||||
| -------- | ------- | ------ | ------------ |
|
||||
| **Authentication** | loginLimiter | 15 min | 5 |
|
||||
| | registerLimiter | 1 hour | 5 |
|
||||
| | forgotPasswordLimiter | 15 min | 5 |
|
||||
| | resetPasswordLimiter | 15 min | 10 |
|
||||
| | refreshTokenLimiter | 15 min | 20 |
|
||||
| | logoutLimiter | 15 min | 10 |
|
||||
| **Public/User Read** | publicReadLimiter | 15 min | 100 |
|
||||
| | userReadLimiter | 15 min | 100 |
|
||||
| | userUpdateLimiter | 15 min | 100 |
|
||||
| **Sensitive Operations** | userSensitiveUpdateLimiter | 1 hour | 5 |
|
||||
| | adminTriggerLimiter | 15 min | 30 |
|
||||
| **AI/Costly** | aiGenerationLimiter | 15 min | 20 |
|
||||
| | geocodeLimiter | 1 hour | 100 |
|
||||
| | priceHistoryLimiter | 15 min | 50 |
|
||||
| **Uploads** | adminUploadLimiter | 15 min | 20 |
|
||||
| | aiUploadLimiter | 15 min | 10 |
|
||||
| | batchLimiter | 15 min | 50 |
|
||||
| **Tracking** | trackingLimiter | 15 min | 200 |
|
||||
| | reactionToggleLimiter | 15 min | 150 |
|
||||
| Category | Limiter | Window | Max Requests |
|
||||
| ------------------------ | -------------------------- | ------ | ------------ |
|
||||
| **Authentication** | loginLimiter | 15 min | 5 |
|
||||
| | registerLimiter | 1 hour | 5 |
|
||||
| | forgotPasswordLimiter | 15 min | 5 |
|
||||
| | resetPasswordLimiter | 15 min | 10 |
|
||||
| | refreshTokenLimiter | 15 min | 20 |
|
||||
| | logoutLimiter | 15 min | 10 |
|
||||
| **Public/User Read** | publicReadLimiter | 15 min | 100 |
|
||||
| | userReadLimiter | 15 min | 100 |
|
||||
| | userUpdateLimiter | 15 min | 100 |
|
||||
| **Sensitive Operations** | userSensitiveUpdateLimiter | 1 hour | 5 |
|
||||
| | adminTriggerLimiter | 15 min | 30 |
|
||||
| **AI/Costly** | aiGenerationLimiter | 15 min | 20 |
|
||||
| | geocodeLimiter | 1 hour | 100 |
|
||||
| | priceHistoryLimiter | 15 min | 50 |
|
||||
| **Uploads** | adminUploadLimiter | 15 min | 20 |
|
||||
| | aiUploadLimiter | 15 min | 10 |
|
||||
| | batchLimiter | 15 min | 50 |
|
||||
| **Tracking** | trackingLimiter | 15 min | 200 |
|
||||
| | reactionToggleLimiter | 15 min | 150 |
|
||||
|
||||
**Test Environment Handling**:
|
||||
|
||||
@@ -140,7 +140,7 @@ sanitizeFilename(filename: string): string
|
||||
|
||||
**Multer Configuration** (`src/middleware/multer.middleware.ts`):
|
||||
|
||||
- MIME type validation via `imageFileFilter` (only image/* allowed)
|
||||
- MIME type validation via `imageFileFilter` (only image/\* allowed)
|
||||
- File size limits (2MB for logos, configurable per upload type)
|
||||
- Unique filenames using timestamps + random suffixes
|
||||
- User-scoped storage paths
|
||||
@@ -203,10 +203,12 @@ Per-request structured logging (ADR-004):
|
||||
|
||||
```typescript
|
||||
import cors from 'cors';
|
||||
app.use(cors({
|
||||
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
|
||||
credentials: true,
|
||||
}));
|
||||
app.use(
|
||||
cors({
|
||||
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
|
||||
credentials: true,
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
2. **Redis-backed rate limiting**: For distributed deployments, use `rate-limit-redis` store
|
||||
|
||||
@@ -16,12 +16,12 @@ We will adopt a hybrid naming convention strategy to explicitly distinguish betw
|
||||
|
||||
1. **Database and AI Types (`snake_case`)**:
|
||||
Interfaces, Type definitions, and Zod schemas that represent raw database rows or direct AI responses **MUST** use `snake_case`.
|
||||
- *Examples*: `AiFlyerDataSchema`, `ExtractedFlyerItemSchema`, `FlyerInsert`.
|
||||
- *Reasoning*: This avoids unnecessary mapping layers when inserting data into the database or parsing AI output. It serves as a visual cue that the data is "raw", "external", or destined for persistence.
|
||||
- _Examples_: `AiFlyerDataSchema`, `ExtractedFlyerItemSchema`, `FlyerInsert`.
|
||||
- _Reasoning_: This avoids unnecessary mapping layers when inserting data into the database or parsing AI output. It serves as a visual cue that the data is "raw", "external", or destined for persistence.
|
||||
|
||||
2. **Internal Application Logic (`camelCase`)**:
|
||||
Variables, function arguments, and processed data structures used within the application logic (Service layer, UI components, utility functions) **MUST** use `camelCase`.
|
||||
- *Reasoning*: This adheres to standard JavaScript/TypeScript practices and maintains consistency with the rest of the ecosystem (React, etc.).
|
||||
- _Reasoning_: This adheres to standard JavaScript/TypeScript practices and maintains consistency with the rest of the ecosystem (React, etc.).
|
||||
|
||||
3. **Boundary Handling**:
|
||||
- For background jobs that primarily move data from AI to DB, preserving `snake_case` is preferred to minimize transformation logic.
|
||||
|
||||
@@ -486,9 +486,9 @@ Attach screenshots for:
|
||||
|
||||
## 🔐 Sign-Off
|
||||
|
||||
**Tester Name**: ******\*\*\*\*******\_\_\_******\*\*\*\*******
|
||||
**Tester Name**: **\*\***\*\*\*\***\*\***\_\_\_**\*\***\*\*\*\***\*\***
|
||||
|
||||
**Date/Time Completed**: ****\*\*\*\*****\_\_\_****\*\*\*\*****
|
||||
**Date/Time Completed**: \***\*\*\*\*\*\*\***\_\_\_\***\*\*\*\*\*\*\***
|
||||
|
||||
**Total Testing Time**: **\_\_** minutes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user