import { DeleteObjectCommand, GetObjectCommand, ListObjectsCommand, S3Client, } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; const BUCKET = "repertory"; const oldItems = []; const s3 = new S3Client({ region: "any", endpoint: "https://gateway.storjshare.io", forcePathStyle: true, credentials: { accessKeyId: process.env.R_AWS_KEY, secretAccessKey: process.env.R_AWS_SECRET, }, }); const cleanOldItems = async () => { console.log(`cleaning|count|${oldItems.length}`); while (oldItems.length > 0) { try { const key = oldItems.pop(); console.log(`cleaning|key|${key}`); await s3.send(new DeleteObjectCommand({ Bucket: BUCKET, Key: key })); } catch (err) { console.error(err); } } }; const createDownloadLink = async (key) => { let filename = key.split("/"); filename = filename[filename.length - 1]; return await getSignedUrl( s3, new GetObjectCommand({ Bucket: BUCKET, Key: key, ResponseContentDisposition: `attachment; filename="${filename}"`, }), { expiresIn: 3600 }, ); }; const getBucketFiles = async (folderName) => { try { folderName = (folderName || "").toLowerCase(); const folderKey = encodeURIComponent(folderName) + "/"; const data = await s3.send( new ListObjectsCommand({ Bucket: BUCKET, Prefix: folderKey, }), ); const contents = Array.isArray(data?.Contents) ? data.Contents : []; const ret = contents .filter((obj) => obj.Key !== folderKey) .map((obj) => { const d = obj.LastModified instanceof Date ? obj.LastModified : new Date(obj.LastModified); return { date: d .toLocaleDateString("en-US", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", }) .replace(/,/g, ""), sort: d.getTime() || 0, name: (obj.Key || "").replace(folderKey, ""), key: obj.Key || "", }; }) .sort((a, b) => (a.sort > b.sort ? -1 : a.sort < b.sort ? 1 : 0)); const itemCount = {}; const ext = ".tar.gz"; // pick tars to keep (apply nightly policy) const tars = ret.filter((it) => it.name.endsWith(ext)); const keepTarKeys = new Set(); for (const t of tars) { if (folderName === "nightly") { const parts = t.name.split("_"); // parts[0]=product, parts[3]=platform, parts[4]=arch const groupId = `${parts[0]}_${parts[3]}_${parts[4]}`; itemCount[groupId] = itemCount[groupId] || 0; if (++itemCount[groupId] <= 3) { keepTarKeys.add(t.key); } else { // mark tar + sidecars old if (!oldItems.includes(t.key)) { oldItems.push(t.key, t.key + ".sha256", t.key + ".sig"); } // windows companion (_setup.exe) + sidecars if (parts[3] === "windows") { const setupKey = t.key.substring(0, t.key.length - ext.length) + "_setup.exe"; const setupItem = ret.find((x) => x.key === setupKey); if (setupItem && !oldItems.includes(setupKey)) { oldItems.push(setupKey, setupKey + ".sha256", setupKey + ".sig"); } } // darwin companion (.dmg) + sidecars if (parts[3] === "darwin") { const dmgKey = t.key.substring(0, t.key.length - ext.length) + ".dmg"; const dmgItem = ret.find((x) => x.key === dmgKey); if (dmgItem && !oldItems.includes(dmgKey)) { oldItems.push(dmgKey, dmgKey + ".sha256", dmgKey + ".sig"); } } } } else { keepTarKeys.add(t.key); } } // fast lookup const byKey = Object.fromEntries(ret.map((r) => [r.key, r])); // build final list by pushing (no in-place splicing) const out = []; for (const t of tars) { if (!keepTarKeys.has(t.key)) continue; const parts = t.name.split("_"); const platform = parts[3]; // 1) tar + sidecars out.push(t); const tarSha = byKey[t.key + ".sha256"]; if (tarSha) out.push(tarSha); const tarSig = byKey[t.key + ".sig"]; if (tarSig) out.push(tarSig); // 2) windows setup + sidecars if (platform === "windows") { const setupKey = t.key.substring(0, t.key.length - ext.length) + "_setup.exe"; const setup = byKey[setupKey]; if (setup) { out.push(setup); const setupSha = byKey[setupKey + ".sha256"]; if (setupSha) out.push(setupSha); const setupSig = byKey[setupKey + ".sig"]; if (setupSig) out.push(setupSig); } } // 3) darwin dmg + sidecars if (platform === "darwin") { const dmgKey = t.key.substring(0, t.key.length - ext.length) + ".dmg"; const dmg = byKey[dmgKey]; if (dmg) { out.push(dmg); const dmgSha = byKey[dmgKey + ".sha256"]; if (dmgSha) out.push(dmgSha); const dmgSig = byKey[dmgKey + ".sig"]; if (dmgSig) out.push(dmgSig); } } } return out; } catch (err) { console.error(err); return []; } }; export { cleanOldItems, createDownloadLink, getBucketFiles };