Files
cloud-server/src/repositories/g8-pricing.ts
kappa 3a8dd705e6 refactor: comprehensive code review fixes (security, performance, QA)
## Security Improvements
- Fix timing attack in verifyApiKey with fixed 256-byte buffer
- Fix sortOrder SQL injection with whitelist validation
- Fix rate limiting bypass for non-Cloudflare traffic (fail-closed)
- Remove stack trace exposure in error responses
- Add request_id for audit trail (X-Request-ID header)
- Sanitize origin header to prevent log injection
- Add content-length validation for /sync endpoint (10KB limit)
- Replace Math.random() with crypto.randomUUID() for sync IDs
- Expand sensitive data masking patterns (8 → 18)

## Performance Improvements
- Reduce rate limiter KV reads from 3 to 1 per request (66% reduction)
- Increase sync batch size from 100 to 500 (80% fewer batches)
- Fix health check N+1 query with efficient JOINs
- Fix COUNT(*) Cartesian product with COUNT(DISTINCT)
- Implement shared logger cache pattern across repositories
- Add CacheService singleton pattern in recommend.ts
- Add composite index for recommendation queries
- Implement Anvil pricing query batching (100 per chunk)

## QA Improvements
- Add BATCH_SIZE bounds validation (1-1000)
- Add pagination bounds (page >= 1, MAX_OFFSET = 100000)
- Add min/max range consistency validation
- Add DB reference validation for singleton services
- Add type guards for database result validation
- Add timeout mechanism for external API calls (10-60s)
- Use SUPPORTED_PROVIDERS constant instead of hardcoded list

## Removed
- Remove Vault integration (using Wrangler secrets)
- Remove 6-hour pricing cron (daily sync only)

## Configuration
- Add idx_instance_types_specs_filter composite index
- Add CORS Access-Control-Expose-Headers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 23:50:37 +09:00

121 lines
3.4 KiB
TypeScript

/**
* G8 Pricing Repository
* Handles CRUD operations for G8 instance pricing data
*/
import { BaseRepository } from './base';
import { G8Pricing, G8PricingInput, RepositoryError, ErrorCodes } from '../types';
export class G8PricingRepository extends BaseRepository<G8Pricing> {
protected tableName = 'g8_pricing';
protected allowedColumns = [
'g8_instance_id',
'region_id',
'hourly_price',
'monthly_price',
'currency',
'available',
];
constructor(db: D1Database) {
super(db);
}
/**
* Find all pricing records for a specific G8 instance
*/
async findByG8Instance(g8InstanceId: number): Promise<G8Pricing[]> {
try {
const result = await this.db
.prepare('SELECT * FROM g8_pricing WHERE g8_instance_id = ?')
.bind(g8InstanceId)
.all<G8Pricing>();
return result.results;
} catch (error) {
this.logger.error('findByG8Instance failed', {
g8InstanceId,
error: error instanceof Error ? error.message : 'Unknown error'
});
throw new RepositoryError(
`Failed to find pricing for G8 instance: ${g8InstanceId}`,
ErrorCodes.DATABASE_ERROR,
error
);
}
}
/**
* Find pricing for a specific G8 instance in a specific region
*/
async findByInstanceAndRegion(g8InstanceId: number, regionId: number): Promise<G8Pricing | null> {
try {
const result = await this.db
.prepare('SELECT * FROM g8_pricing WHERE g8_instance_id = ? AND region_id = ?')
.bind(g8InstanceId, regionId)
.first<G8Pricing>();
return result || null;
} catch (error) {
this.logger.error('findByInstanceAndRegion failed', {
g8InstanceId,
regionId,
error: error instanceof Error ? error.message : 'Unknown error'
});
throw new RepositoryError(
`Failed to find pricing for G8 instance ${g8InstanceId} in region ${regionId}`,
ErrorCodes.DATABASE_ERROR,
error
);
}
}
/**
* Upsert multiple G8 pricing records (batch operation)
*/
async upsertMany(pricingData: G8PricingInput[]): Promise<number> {
if (pricingData.length === 0) {
return 0;
}
try {
const statements = pricingData.map((pricing) => {
return this.db.prepare(
`INSERT INTO g8_pricing (
g8_instance_id, region_id, hourly_price, monthly_price,
currency, available
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(g8_instance_id, region_id)
DO UPDATE SET
hourly_price = excluded.hourly_price,
monthly_price = excluded.monthly_price,
currency = excluded.currency,
available = excluded.available`
).bind(
pricing.g8_instance_id,
pricing.region_id,
pricing.hourly_price,
pricing.monthly_price,
pricing.currency,
pricing.available
);
});
const successCount = await this.executeBatchCount(statements);
this.logger.info('Upserted G8 pricing records', { count: successCount });
return successCount;
} catch (error) {
this.logger.error('upsertMany failed', {
count: pricingData.length,
error: error instanceof Error ? error.message : 'Unknown error'
});
throw new RepositoryError(
'Failed to upsert G8 pricing records',
ErrorCodes.TRANSACTION_FAILED,
error
);
}
}
}