German for 'quack', matching the Ente (German for 'duck') naming. All references updated: package name, CLI binary, X-Client-Package header, test descriptions, temp dir prefixes, README, Makefile docker tag.
116 lines
4.1 KiB
TypeScript
116 lines
4.1 KiB
TypeScript
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);
|
|
});
|