CognxSafeTrack
chore: execute Sprint 38 technical debt resolution (Type Safety, Zod validation, Vitest, Mock LLM extracted)
d9879cf | import dotenv from 'dotenv'; | |
| dotenv.config({ path: '/Volumes/sms/edtech/.env' }); | |
| const { R2_ACCOUNT_ID, R2_BUCKET, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_PUBLIC_URL } = process.env; | |
| if (!R2_ACCOUNT_ID || !R2_BUCKET || !R2_ACCESS_KEY_ID || !R2_SECRET_ACCESS_KEY || !R2_PUBLIC_URL) { | |
| console.error('β Missing R2 credentials'); process.exit(1); | |
| } | |
| import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; | |
| import fs from 'fs'; | |
| import path from 'path'; | |
| const ARTIFACTS = '/Users/macbookair/.gemini/antigravity/brain/ea618d02-1ee0-47c1-a2b7-aed00a3604bc'; | |
| const TRACKS_DIR = '/Volumes/sms/edtech/packages/database/content/tracks'; | |
| const base = R2_PUBLIC_URL!.replace(/\/$/, ''); | |
| const client = new S3Client({ | |
| region: 'auto', | |
| endpoint: `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, | |
| credentials: { accessKeyId: R2_ACCESS_KEY_ID!, secretAccessKey: R2_SECRET_ACCESS_KEY! }, | |
| }); | |
| const IMAGE_MAP = [ | |
| { src: 't4_j3_credibilite_1772319157261.png', r2: 'images/t4/bes3_credibilite.png', track: 'T4', day: 3 }, | |
| { src: 't4_j4_organisation_1772319168678.png', r2: 'images/t4/bes4_organisation.png', track: 'T4', day: 4 }, | |
| { src: 't4_j5_stock_1772319180883.png', r2: 'images/t4/bes5_stock.png', track: 'T4', day: 5 }, | |
| { src: 't4_j6_cahier_cash_1772319194657.png', r2: 'images/t4/bes6_cahier_cash.png', track: 'T4', day: 6 }, | |
| { src: 't4_j7_plan_action_1772319265677.png', r2: 'images/t4/bes7_plan_action.png', track: 'T4', day: 7 }, | |
| { src: 't5_j1_banque_1772319281497.png', r2: 'images/t5/bes1_banque.png', track: 'T5', day: 1 }, | |
| { src: 't5_j2_5c_credit_1772319295980.png', r2: 'images/t5/bes2_5c_credit.png', track: 'T5', day: 2 }, | |
| { src: 't5_j3_chiffres_1772319309609.png', r2: 'images/t5/bes3_chiffres.png', track: 'T5', day: 3 }, | |
| { src: 't5_j4_garanties_1772319396014.png', r2: 'images/t5/bes4_garanties.png', track: 'T5', day: 4 }, | |
| { src: 't5_j5_pitch_financeur_1772319407304.png', r2: 'images/t5/bes5_pitch_financeur.png', track: 'T5', day: 5 }, | |
| { src: 't5_j6_certification_1772319420509.png', r2: 'images/t5/bes6_certification.png', track: 'T5', day: 6 }, | |
| ]; | |
| async function upload(srcFile: string, r2Key: string): Promise<string> { | |
| const buf = fs.readFileSync(path.join(ARTIFACTS, srcFile)); | |
| await client.send(new PutObjectCommand({ | |
| Bucket: R2_BUCKET!, Key: r2Key, Body: buf, | |
| ContentType: 'image/png', CacheControl: 'public, max-age=31536000' | |
| })); | |
| return `${base}/${r2Key}`; | |
| } | |
| function inject(track: string, day: number, imageUrl: string) { | |
| for (const lang of ['FR', 'WO']) { | |
| const fp = path.join(TRACKS_DIR, `${track}-${lang}.json`); | |
| if (!fs.existsSync(fp)) continue; | |
| const data = JSON.parse(fs.readFileSync(fp, 'utf-8')); | |
| for (const d of data.days) { if (d.dayNumber === day) d.imageUrl = imageUrl; } | |
| fs.writeFileSync(fp, JSON.stringify(data, null, 2)); | |
| console.log(` π ${track}-${lang}.json J${day} β ${imageUrl.split('/').pop()}`); | |
| } | |
| } | |
| async function main() { | |
| console.log(`\nπ Uploading ${IMAGE_MAP.length} images to R2...\n`); | |
| let ok = 0; | |
| for (const img of IMAGE_MAP) { | |
| try { | |
| const url = await upload(img.src, img.r2); | |
| console.log(`β ${img.r2}`); | |
| inject(img.track, img.day, url); | |
| ok++; | |
| } catch (e: unknown) { | |
| console.error(`β ${img.r2}: ${(e instanceof Error ? (e instanceof Error ? e.message : String(e)) : String(e))}`); | |
| } | |
| } | |
| console.log(`\nβ Done: ${ok}/${IMAGE_MAP.length} uploaded and injected.`); | |
| } | |
| main().catch(e => { console.error(e); process.exit(1); }); | |