|
1 | 1 | import { FastifyInstance, FastifyReply } from 'fastify'; |
| 2 | +import { Storage } from '@google-cloud/storage'; |
2 | 3 | import { retryFetch } from '../integrations/retry'; |
3 | | -import { WEBAPP_MAGIC_IMAGE_PREFIX } from '../config'; |
| 4 | +import { |
| 5 | + WEBAPP_MAGIC_IMAGE_PREFIX, |
| 6 | + YEAR_IN_REVIEW_BUCKET_NAME, |
| 7 | +} from '../config'; |
4 | 8 | import createOrGetConnection from '../db'; |
5 | 9 | import { User } from '../entity'; |
6 | 10 |
|
| 11 | +const storage = new Storage(); |
| 12 | + |
7 | 13 | // Record types matching the webapp's RecordType enum |
8 | 14 | const RecordType = { |
9 | 15 | YEAR_ACTIVE: 'yearActive', |
@@ -127,6 +133,29 @@ const MOCK_LOG_DATA = { |
127 | 133 | shareCount: 24853, |
128 | 134 | }; |
129 | 135 |
|
| 136 | +/** |
| 137 | + * Fetch user's year-in-review log data from GCS bucket. |
| 138 | + * Returns null if the file doesn't exist. |
| 139 | + */ |
| 140 | +async function fetchLogDataFromGCS( |
| 141 | + userId: string, |
| 142 | +): Promise<typeof MOCK_LOG_DATA | null> { |
| 143 | + try { |
| 144 | + const bucket = storage.bucket(YEAR_IN_REVIEW_BUCKET_NAME); |
| 145 | + const file = bucket.file(`2025/first_30/${userId}.json`); |
| 146 | + |
| 147 | + const [exists] = await file.exists(); |
| 148 | + if (!exists) { |
| 149 | + return null; |
| 150 | + } |
| 151 | + |
| 152 | + const [content] = await file.download(); |
| 153 | + return JSON.parse(content.toString()); |
| 154 | + } catch (error) { |
| 155 | + return null; |
| 156 | + } |
| 157 | +} |
| 158 | + |
130 | 159 | // Valid card types for log share images (welcome is not shareable) |
131 | 160 | const VALID_CARD_TYPES = [ |
132 | 161 | 'total-impact', |
@@ -228,22 +257,24 @@ export default async function (fastify: FastifyInstance): Promise<void> { |
228 | 257 | /** |
229 | 258 | * GET /log |
230 | 259 | * Returns the user's log data for the year. |
| 260 | + * Accepts optional userId query param to fetch specific user's data from GCS. |
231 | 261 | * Returns 404 if user doesn't have enough data (no JSON file exists). |
232 | 262 | */ |
233 | | - fastify.get('/', async (req, res) => { |
234 | | - if (!req.userId) { |
235 | | - return res.status(401).send({ error: 'Unauthorized' }); |
236 | | - } |
237 | | - |
238 | | - // TODO: Replace with actual JSON file lookup based on req.userId |
239 | | - // In production, this will check if the user's JSON file exists |
240 | | - const userDataExists = true; |
241 | | - |
242 | | - if (!userDataExists) { |
243 | | - return res.status(404).send({ error: 'No log data available' }); |
| 263 | + fastify.get<{ |
| 264 | + Querystring: { userId?: string }; |
| 265 | + }>('/', async (req, res) => { |
| 266 | + const { userId } = req.query; |
| 267 | + |
| 268 | + // If userId query param is provided, fetch from GCS |
| 269 | + if (userId) { |
| 270 | + const logData = await fetchLogDataFromGCS(userId); |
| 271 | + if (!logData) { |
| 272 | + return res.status(404).send({ error: 'No log data available' }); |
| 273 | + } |
| 274 | + return res.send(logData); |
244 | 275 | } |
245 | 276 |
|
246 | | - // TODO: Replace mock data with actual user data from JSON file |
| 277 | + // Fall back to mock data when no userId provided |
247 | 278 | return res.send(MOCK_LOG_DATA); |
248 | 279 | }); |
249 | 280 |
|
@@ -288,17 +319,8 @@ export default async function (fastify: FastifyInstance): Promise<void> { |
288 | 319 | return res.status(404).send({ error: 'User not found' }); |
289 | 320 | } |
290 | 321 |
|
291 | | - // TODO: Replace with actual JSON file lookup based on req.userId |
292 | | - // In production, this will check if the user's JSON file exists |
293 | | - const userDataExists = true; |
294 | | - |
295 | | - if (!userDataExists) { |
296 | | - return res.status(404).send({ error: 'No log data available' }); |
297 | | - } |
298 | | - |
299 | | - // Fetch user's log data |
300 | | - // TODO: Replace with actual data fetching from JSON file |
301 | | - const logData = MOCK_LOG_DATA; |
| 322 | + // Fetch user's log data from GCS, fall back to mock data |
| 323 | + const logData = (await fetchLogDataFromGCS(req.userId)) ?? MOCK_LOG_DATA; |
302 | 324 |
|
303 | 325 | // Extract only the data needed for this card type |
304 | 326 | const cardData = extractCardData(card as CardType, logData); |
|
0 commit comments