CognxSafeTrack
chore: execute Sprint 38 technical debt resolution (Type Safety, Zod validation, Vitest, Mock LLM extracted)
d9879cf | import dotenv from 'dotenv'; | |
| // Load root .env first | |
| 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; | |
| // ββ Fast fail: show config status first ββββββββββββββββββββββββββββββββββββββ | |
| console.log('\nπ R2 Config Check:'); | |
| console.log(` R2_ACCOUNT_ID: ${R2_ACCOUNT_ID ? 'β ' + R2_ACCOUNT_ID : 'β MISSING'}`); | |
| console.log(` R2_BUCKET: ${R2_BUCKET ? 'β ' + R2_BUCKET : 'β MISSING'}`); | |
| console.log(` R2_ACCESS_KEY_ID: ${R2_ACCESS_KEY_ID ? 'β SET' : 'β MISSING'}`); | |
| console.log(` R2_SECRET_ACCESS_KEY:${R2_SECRET_ACCESS_KEY ? 'β SET' : 'β MISSING'}`); | |
| console.log(` R2_PUBLIC_URL: ${R2_PUBLIC_URL ? 'β ' + R2_PUBLIC_URL : 'β MISSING'}`); | |
| if (!R2_ACCOUNT_ID || !R2_BUCKET || !R2_ACCESS_KEY_ID || !R2_SECRET_ACCESS_KEY || !R2_PUBLIC_URL) { | |
| console.error('\nβ Missing R2 credentials. Add these to /Volumes/sms/edtech/.env:'); | |
| console.error('\n R2_ACCOUNT_ID=<your_cloudflare_account_id>'); | |
| console.error(' R2_BUCKET=<your_bucket_name>'); | |
| console.error(' R2_ACCESS_KEY_ID=<your_r2_api_token_key_id>'); | |
| console.error(' R2_SECRET_ACCESS_KEY=<your_r2_api_token_secret>'); | |
| console.error(' R2_PUBLIC_URL=https://r2.xamle.sn'); | |
| console.error('\n Find them on: dash.cloudflare.com β R2 β Manage R2 API Tokens'); | |
| process.exit(1); | |
| } | |
| import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; | |
| import fs from 'fs'; | |
| import path from 'path'; | |
| const ARTIFACTS_DIR = '/Users/macbookair/.gemini/antigravity/brain/ea618d02-1ee0-47c1-a2b7-aed00a3604bc'; | |
| const IMAGE_MAP = [ | |
| { src: 'xaml_t1_j1_activity_1772065123770.png', dest: 'images/t1/bes1_activity.png' }, | |
| { src: 'xaml_t1_j1_5_remediation_1772065225648.png', dest: 'images/t1/bes1_5_remediation.png' }, | |
| { src: 'xaml_t1_j2_customer_1772065290030.png', dest: 'images/t1/bes2_customer.png' }, | |
| { src: 'xaml_t1_j3_problem_1772065392345.png', dest: 'images/t1/bes3_problem.png' }, | |
| { src: 'xaml_t1_j4_solution_1772065519438.png', dest: 'images/t1/bes4_solution.png' }, | |
| { src: 'xaml_t1_j5_differentiation_v2_1772065582075.png', dest: 'images/t1/bes5_differentiation.png' }, | |
| { src: 'xaml_t1_j6_pricing_v2_1772065655264.png', dest: 'images/t1/bes6_pricing.png' }, | |
| { src: 'xaml_t1_j7_channel_sales_v2_1772065752541.png', dest: 'images/t1/bes7_channel.png' }, | |
| { src: 'xaml_t1_j8_trust_1772065782385.png', dest: 'images/t1/bes8_trust.png' }, | |
| { src: 'xaml_t1_j9_pitch_1772065807317.png', dest: 'images/t1/bes9_pitch.png' }, | |
| { src: 'xaml_t1_j10_objections_1772065830459.png', dest: 'images/t1/bes10_objections.png' }, | |
| { src: 'xaml_t1_j11_plan_1772065894417.png', dest: 'images/t1/bes11_plan.png' }, | |
| { src: 'xaml_t1_j12_success_1772065982976.png', dest: 'images/t1/bes12_success.png' }, | |
| ]; | |
| async function main() { | |
| const { R2_ACCOUNT_ID, R2_BUCKET, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_PUBLIC_URL } = process.env; | |
| console.log('π R2 Config Check:'); | |
| console.log(' R2_ACCOUNT_ID:', R2_ACCOUNT_ID ? 'β SET' : 'β MISSING'); | |
| console.log(' R2_BUCKET:', R2_BUCKET ? `β ${R2_BUCKET}` : 'β MISSING'); | |
| console.log(' R2_ACCESS_KEY_ID:', R2_ACCESS_KEY_ID ? 'β SET' : 'β MISSING'); | |
| console.log(' R2_SECRET_ACCESS_KEY:', R2_SECRET_ACCESS_KEY ? 'β SET' : 'β MISSING'); | |
| console.log(' R2_PUBLIC_URL:', R2_PUBLIC_URL || 'β MISSING'); | |
| if (!R2_ACCOUNT_ID || !R2_BUCKET || !R2_ACCESS_KEY_ID || !R2_SECRET_ACCESS_KEY) { | |
| console.error('\nβ Missing R2 environment variables. Stopping.'); | |
| process.exit(1); | |
| } | |
| 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 }, | |
| }); | |
| console.log('\nπ Uploading 13 T1 images to R2...\n'); | |
| let ok = 0, failed = 0; | |
| for (const { src, dest } of IMAGE_MAP) { | |
| const srcPath = path.join(ARTIFACTS_DIR, src); | |
| if (!fs.existsSync(srcPath)) { | |
| console.warn(`β οΈ Not found: ${src}`); | |
| failed++; | |
| continue; | |
| } | |
| try { | |
| const buf = fs.readFileSync(srcPath); | |
| await client.send(new PutObjectCommand({ | |
| Bucket: R2_BUCKET, | |
| Key: dest, | |
| Body: buf, | |
| ContentType: 'image/png', | |
| CacheControl: 'public, max-age=31536000', | |
| })); | |
| const publicUrl = `${(R2_PUBLIC_URL || '').replace(/\/$/, '')}/${dest}`; | |
| console.log(`β ${dest}`); | |
| console.log(` URL: ${publicUrl}`); | |
| ok++; | |
| } catch (e: unknown) { | |
| console.error(`β ${dest}: ${(e instanceof Error ? (e instanceof Error ? e.message : String(e)) : String(e))}`); | |
| failed++; | |
| } | |
| } | |
| console.log(`\nββ RΓSULTAT ββββββββββββββββββββ`); | |
| console.log(`β Uploaded: ${ok}/${IMAGE_MAP.length}`); | |
| if (failed > 0) { | |
| console.log(`β Failed: ${failed}`); | |
| process.exit(1); | |
| } else { | |
| console.log(`\nπ Toutes les images T1 sont maintenant sur R2 !`); | |
| console.log(` Les leΓ§ons WhatsApp du module T1 afficheront bien les images.`); | |
| } | |
| } | |
| main().catch(e => { console.error(e); process.exit(1); }); | |