Fix GitHub Actions build by adding missing repository files

The build was failing because repository files were ignored by .gitignore:
- backend/src/features/*/data/*.repository.ts files were excluded by 'data/' pattern
- These files exist locally but were missing in CI, causing TS2307 module errors
- Controllers and services import these repositories, causing cascade failures

Changes:
- Updated .gitignore to allow TypeScript files in feature data directories
- Added fuel-logs.repository.ts, stations.repository.ts, vehicles.repository.ts
- Docker build now succeeds (tested with --no-cache)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2025-08-24 10:03:09 -05:00
parent b534e92636
commit 9ad9e2ee7c
4 changed files with 504 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
/**
* @ai-summary Data access layer for stations
*/
import { Pool } from 'pg';
import { Station, SavedStation } from '../domain/stations.types';
export class StationsRepository {
constructor(private pool: Pool) {}
async cacheStation(station: Station): Promise<void> {
const query = `
INSERT INTO station_cache (
place_id, name, address, latitude, longitude,
price_regular, price_premium, price_diesel, rating, photo_url
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT (place_id) DO UPDATE
SET name = $2, address = $3, latitude = $4, longitude = $5,
rating = $9, photo_url = $10, cached_at = NOW()
`;
await this.pool.query(query, [
station.placeId,
station.name,
station.address,
station.latitude,
station.longitude,
station.priceRegular,
station.pricePremium,
station.priceDiesel,
station.rating,
station.photoUrl
]);
}
async getCachedStation(placeId: string): Promise<Station | null> {
const query = 'SELECT * FROM station_cache WHERE place_id = $1';
const result = await this.pool.query(query, [placeId]);
if (result.rows.length === 0) {
return null;
}
return this.mapCacheRow(result.rows[0]);
}
async saveStation(userId: string, placeId: string, data?: { nickname?: string; notes?: string; isFavorite?: boolean }): Promise<SavedStation> {
const query = `
INSERT INTO saved_stations (user_id, place_id, nickname, notes, is_favorite)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (user_id, place_id) DO UPDATE
SET nickname = COALESCE($3, saved_stations.nickname),
notes = COALESCE($4, saved_stations.notes),
is_favorite = COALESCE($5, saved_stations.is_favorite),
updated_at = NOW()
RETURNING *
`;
const result = await this.pool.query(query, [
userId,
placeId,
data?.nickname,
data?.notes,
data?.isFavorite || false
]);
return this.mapSavedRow(result.rows[0]);
}
async getUserSavedStations(userId: string): Promise<SavedStation[]> {
const query = `
SELECT * FROM saved_stations
WHERE user_id = $1
ORDER BY is_favorite DESC, created_at DESC
`;
const result = await this.pool.query(query, [userId]);
return result.rows.map(row => this.mapSavedRow(row));
}
async deleteSavedStation(userId: string, placeId: string): Promise<boolean> {
const query = 'DELETE FROM saved_stations WHERE user_id = $1 AND place_id = $2';
const result = await this.pool.query(query, [userId, placeId]);
return (result.rowCount ?? 0) > 0;
}
private mapCacheRow(row: any): Station {
return {
id: row.id,
placeId: row.place_id,
name: row.name,
address: row.address,
latitude: parseFloat(row.latitude),
longitude: parseFloat(row.longitude),
priceRegular: row.price_regular ? parseFloat(row.price_regular) : undefined,
pricePremium: row.price_premium ? parseFloat(row.price_premium) : undefined,
priceDiesel: row.price_diesel ? parseFloat(row.price_diesel) : undefined,
rating: row.rating ? parseFloat(row.rating) : undefined,
photoUrl: row.photo_url,
lastUpdated: row.cached_at
};
}
private mapSavedRow(row: any): SavedStation {
return {
id: row.id,
userId: row.user_id,
stationId: row.place_id,
nickname: row.nickname,
notes: row.notes,
isFavorite: row.is_favorite,
createdAt: row.created_at,
updatedAt: row.updated_at
};
}
}