"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findBlobRefs = void 0;
exports.default = default_1;
const cid_1 = require("multiformats/cid");
const common_1 = require("@atproto/common");
const lexicon_1 = require("@atproto/lexicon");
const repo_1 = require("@atproto/repo");
const syntax_1 = require("@atproto/syntax");
const xrpc_server_1 = require("@atproto/xrpc-server");
const auth_scope_1 = require("../../../../auth-scope");
function default_1(server, ctx) {
    server.com.atproto.repo.importRepo({
        opts: {
            blobLimit: ctx.cfg.service.maxImportSize,
        },
        auth: ctx.authVerifier.authorization({
            checkTakedown: true,
            scopes: auth_scope_1.ACCESS_FULL,
            authorize: (permissions) => {
                permissions.assertAccount({ attr: 'repo', action: 'manage' });
            },
        }),
        handler: async ({ input, auth }) => {
            if (!ctx.cfg.service.acceptingImports) {
                throw new xrpc_server_1.InvalidRequestError('Service is not accepting repo imports');
            }
            const { did } = auth.credentials;
            // @NOTE process as much as we can before the transaction, in particular
            // the reading of the body stream.
            const { roots, blocks } = await (0, repo_1.readCarStream)(input.body);
            if (roots.length !== 1) {
                await blocks.dump();
                throw new xrpc_server_1.InvalidRequestError('expected one root');
            }
            const blockMap = new repo_1.BlockMap();
            for await (const block of blocks) {
                blockMap.set(block.cid, block.bytes);
            }
            await ctx.actorStore.transact(did, async (store) => {
                const now = new Date().toISOString();
                const rev = common_1.TID.nextStr();
                const did = store.repo.did;
                const currRepo = await store.repo.maybeLoadRepo();
                const diff = await (0, repo_1.verifyDiff)(currRepo, blockMap, roots[0], undefined, undefined, { ensureLeaves: false });
                diff.commit.rev = rev;
                await store.repo.storage.applyCommit(diff.commit, currRepo === null);
                // @NOTE There is no point in performing the following concurrently
                // since better-sqlite3 is synchronous.
                for (const write of diff.writes) {
                    const uri = syntax_1.AtUri.make(did, write.collection, write.rkey);
                    if (write.action === repo_1.WriteOpAction.Delete) {
                        await store.record.deleteRecord(uri);
                    }
                    else {
                        let parsedRecord;
                        try {
                            // @NOTE getAndParseRecord returns a promise for historical
                            // reasons but it's internal processing is actually synchronous.
                            const parsed = await (0, repo_1.getAndParseRecord)(blockMap, write.cid);
                            parsedRecord = parsed.record;
                        }
                        catch {
                            throw new xrpc_server_1.InvalidRequestError(`Could not parse record at '${write.collection}/${write.rkey}'`);
                        }
                        await store.record.indexRecord(uri, write.cid, parsedRecord, write.action, rev, now);
                        const recordBlobs = (0, exports.findBlobRefs)(parsedRecord);
                        await store.repo.blob.insertBlobs(uri.toString(), recordBlobs);
                    }
                }
            });
        },
    });
}
const findBlobRefs = (val, layer = 0) => {
    if (layer > 32) {
        return [];
    }
    // walk arrays
    if (Array.isArray(val)) {
        return val.flatMap((item) => (0, exports.findBlobRefs)(item, layer + 1));
    }
    // objects
    if (val && typeof val === 'object') {
        // convert blobs, leaving the original encoding so that we don't change CIDs on re-encode
        if (val instanceof lexicon_1.BlobRef) {
            return [val];
        }
        // retain cids & bytes
        if (cid_1.CID.asCID(val) || val instanceof Uint8Array) {
            return [];
        }
        return Object.values(val).flatMap((item) => (0, exports.findBlobRefs)(item, layer + 1));
    }
    // pass through
    return [];
};
exports.findBlobRefs = findBlobRefs;
//# sourceMappingURL=importRepo.js.map