import { init } from "../../src/crypto/index.js"; import { ApiClient } from "../../src/api/client.js"; import { beginLogin } from "../../src/auth/login.js"; import { unwrapAuth } from "../../src/auth/unwrap.js"; import { decryptCollection, decryptFile } from "../../src/model/index.js"; import { downloadFile } from "../../src/download/index.js"; import type { RawCollection, RawEnteFile } from "../../src/model/index.js"; // Dev account — not a secret, throwaway account for integration testing. const EMAIL = "entedev2026jp@acidhou.se"; const PASSWORD = "loldongs"; const RECOVERY_KEY = "deliver have behave collect void chicken boring embrace coast reflect squeeze cotton dish resemble license remain quick dwarf plastic ensure amused cry nasty equip"; void RECOVERY_KEY; const main = async () => { await init(); const api = new ApiClient(); console.log(`Logging in as ${EMAIL}...`); const challenge = await beginLogin(api, EMAIL, PASSWORD); if (challenge.kind !== "complete") { console.error("Expected complete login, got:", challenge.kind); process.exit(1); } const { masterKey, token } = await unwrapAuth(challenge.response, PASSWORD); api.setAuthToken(token); console.log("Logged in, user ID:", challenge.response.id); // Fetch and decrypt collections console.log("\nFetching collections..."); const { collections: rawCollections } = await api.getJSON<{ collections: RawCollection[]; }>("/collections/v2", { sinceTime: 0 }); const userID = challenge.response.id; const collections = rawCollections.map((raw) => decryptCollection(raw, masterKey, userID), ); console.log(`${collections.length} collection(s):`); for (const c of collections) { console.log( ` [${c.type}] "${c.name}" (id=${c.id}, shared=${c.isShared})`, ); } // Fetch and decrypt files from the first non-empty collection for (const col of collections) { console.log(`\nFetching files from "${col.name}" (id=${col.id})...`); const { diff: rawFiles } = await api.getJSON<{ diff: RawEnteFile[]; hasMore: boolean; }>("/collections/v2/diff", { collectionID: col.id, sinceTime: 0, }); if (rawFiles.length === 0) { console.log(" (empty)"); continue; } const files = rawFiles .filter((f) => !f.isDeleted) .map((raw) => decryptFile(raw, col.key)); console.log(` ${files.length} file(s):`); for (const f of files.slice(0, 10)) { console.log( ` ${f.metadata.title} [${f.metadata.fileType}] id=${f.id}`, ); if (f.metadata.latitude !== undefined) { console.log( ` location: ${f.metadata.latitude}, ${f.metadata.longitude}`, ); } } if (files.length > 10) { console.log(` ... and ${files.length - 10} more`); } // Download the first file to a temp directory if (files.length > 0) { const first = files[0]!; const { mkdtempSync, statSync } = await import("node:fs"); const { join } = await import("node:path"); const { tmpdir } = await import("node:os"); const outDir = mkdtempSync(join(tmpdir(), "quak-live-test-")); const outPath = `${outDir}/${first.metadata.title}`; console.log(`\n Downloading "${first.metadata.title}"...`); const result = await downloadFile(api, first, outPath); const stat = statSync(result.path); console.log( ` Saved to ${result.path} (${result.bytesWritten} bytes decrypted, ${stat.size} on disk)`, ); if (result.bytesWritten < 1000) { console.error( " WARNING: file seems too small, possible decryption issue", ); } } break; } console.log("\nLive integration test passed."); }; main().catch((err) => { console.error("FAILED:", err); process.exit(1); });