Files
cloud-server/src/repositories/providers.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

143 lines
3.4 KiB
TypeScript

/**
* Providers Repository
* Handles CRUD operations for cloud providers
*/
import { BaseRepository } from './base';
import { Provider, ProviderInput, RepositoryError, ErrorCodes } from '../types';
export class ProvidersRepository extends BaseRepository<Provider> {
protected tableName = 'providers';
protected allowedColumns = [
'name',
'display_name',
'api_base_url',
'last_sync_at',
'sync_status',
'sync_error',
];
/**
* Find provider by name
*/
async findByName(name: string): Promise<Provider | null> {
try {
const result = await this.db
.prepare('SELECT * FROM providers WHERE name = ?')
.bind(name)
.first<Provider>();
return result || null;
} catch (error) {
this.logger.error('findByName failed', {
name,
error: error instanceof Error ? error.message : 'Unknown error'
});
throw new RepositoryError(
`Failed to find provider by name: ${name}`,
ErrorCodes.DATABASE_ERROR,
error
);
}
}
/**
* Update sync status for a provider
*/
async updateSyncStatus(
name: string,
status: 'pending' | 'syncing' | 'success' | 'error',
error?: string
): Promise<Provider> {
try {
const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
const result = await this.db
.prepare(
`UPDATE providers
SET sync_status = ?,
sync_error = ?,
last_sync_at = ?
WHERE name = ?
RETURNING *`
)
.bind(status, error || null, now, name)
.first<Provider>();
if (!result) {
throw new RepositoryError(
`Provider not found: ${name}`,
ErrorCodes.NOT_FOUND
);
}
return result;
} catch (error) {
this.logger.error('updateSyncStatus failed', {
name,
status,
error: error instanceof Error ? error.message : 'Unknown error'
});
if (error instanceof RepositoryError) {
throw error;
}
throw new RepositoryError(
`Failed to update sync status for provider: ${name}`,
ErrorCodes.DATABASE_ERROR,
error
);
}
}
/**
* Get all providers with specific sync status
*/
async findByStatus(status: Provider['sync_status']): Promise<Provider[]> {
try {
const result = await this.db
.prepare('SELECT * FROM providers WHERE sync_status = ?')
.bind(status)
.all<Provider>();
return result.results;
} catch (error) {
this.logger.error('findByStatus failed', {
status,
error: error instanceof Error ? error.message : 'Unknown error'
});
throw new RepositoryError(
`Failed to find providers by status: ${status}`,
ErrorCodes.DATABASE_ERROR,
error
);
}
}
/**
* Create or update a provider
*/
async upsert(data: ProviderInput): Promise<Provider> {
try {
const existing = await this.findByName(data.name);
if (existing) {
return await this.update(existing.id, data);
}
return await this.create(data);
} catch (error) {
this.logger.error('upsert failed', {
name: data.name,
error: error instanceof Error ? error.message : 'Unknown error'
});
throw new RepositoryError(
`Failed to upsert provider: ${data.name}`,
ErrorCodes.DATABASE_ERROR,
error
);
}
}
}