edtech / apps /api /src /scripts /upload-t4t5-final.ts
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); });