"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SharedLinkService = void 0;
const common_1 = require("@nestjs/common");
const asset_ids_response_dto_1 = require("../dtos/asset-ids.response.dto");
const shared_link_dto_1 = require("../dtos/shared-link.dto");
const enum_1 = require("../enum");
const base_service_1 = require("./base.service");
const misc_1 = require("../utils/misc");
let SharedLinkService = class SharedLinkService extends base_service_1.BaseService {
    async getAll(auth, { id, albumId }) {
        return this.sharedLinkRepository
            .getAll({ userId: auth.user.id, id, albumId })
            .then((links) => links.map((link) => (0, shared_link_dto_1.mapSharedLink)(link, { stripAssetMetadata: false })));
    }
    async login(auth, dto) {
        if (!auth.sharedLink) {
            throw new common_1.ForbiddenException();
        }
        const sharedLink = await this.findOrFail(auth.user.id, auth.sharedLink.id);
        const { id, password } = sharedLink;
        if (!password) {
            throw new common_1.BadRequestException('Shared link is not password protected');
        }
        if (password !== dto.password) {
            throw new common_1.UnauthorizedException('Invalid password');
        }
        return {
            sharedLink: (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: !sharedLink.showExif }),
            token: this.asToken({ id, password }),
        };
    }
    async getMine(auth, authTokens) {
        if (!auth.sharedLink) {
            throw new common_1.ForbiddenException();
        }
        const sharedLink = await this.findOrFail(auth.user.id, auth.sharedLink.id);
        const { id, password } = sharedLink;
        if (password && !authTokens.includes(this.asToken({ id, password }))) {
            throw new common_1.UnauthorizedException('Password required');
        }
        return (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: !sharedLink.showExif });
    }
    async get(auth, id) {
        const sharedLink = await this.findOrFail(auth.user.id, id);
        return (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: false });
    }
    async create(auth, dto) {
        switch (dto.type) {
            case enum_1.SharedLinkType.Album: {
                if (!dto.albumId) {
                    throw new common_1.BadRequestException('Invalid albumId');
                }
                await this.requireAccess({ auth, permission: enum_1.Permission.AlbumShare, ids: [dto.albumId] });
                break;
            }
            case enum_1.SharedLinkType.Individual: {
                if (!dto.assetIds || dto.assetIds.length === 0) {
                    throw new common_1.BadRequestException('Invalid assetIds');
                }
                await this.requireAccess({ auth, permission: enum_1.Permission.AssetShare, ids: dto.assetIds });
                break;
            }
        }
        try {
            const sharedLink = await this.sharedLinkRepository.create({
                key: this.cryptoRepository.randomBytes(50),
                userId: auth.user.id,
                type: dto.type,
                albumId: dto.albumId || null,
                assetIds: dto.assetIds,
                description: dto.description || null,
                password: dto.password,
                expiresAt: dto.expiresAt || null,
                allowUpload: dto.allowUpload ?? true,
                allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true),
                showExif: dto.showMetadata ?? true,
                slug: dto.slug || null,
            });
            return (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: false });
        }
        catch (error) {
            this.handleError(error);
        }
    }
    handleError(error) {
        if (error.constraint_name === 'shared_link_slug_uq') {
            throw new common_1.BadRequestException('Shared link with this slug already exists');
        }
        throw error;
    }
    async update(auth, id, dto) {
        await this.findOrFail(auth.user.id, id);
        try {
            const sharedLink = await this.sharedLinkRepository.update({
                id,
                userId: auth.user.id,
                description: dto.description,
                password: dto.password,
                expiresAt: dto.changeExpiryTime && !dto.expiresAt ? null : dto.expiresAt,
                allowUpload: dto.allowUpload,
                allowDownload: dto.allowDownload,
                showExif: dto.showMetadata,
                slug: dto.slug || null,
            });
            return (0, shared_link_dto_1.mapSharedLink)(sharedLink, { stripAssetMetadata: false });
        }
        catch (error) {
            this.handleError(error);
        }
    }
    async remove(auth, id) {
        const sharedLink = await this.findOrFail(auth.user.id, id);
        await this.sharedLinkRepository.remove(sharedLink.id);
    }
    async findOrFail(userId, id) {
        const sharedLink = await this.sharedLinkRepository.get(userId, id);
        if (!sharedLink) {
            throw new common_1.BadRequestException('Shared link not found');
        }
        return sharedLink;
    }
    async addAssets(auth, id, dto) {
        if (auth.sharedLink) {
            this.logger.deprecate('Assets uploaded using shared link authentication are now automatically added to the shared link during upload and in the next major release this endpoint will no longer accept shared link authentication');
        }
        const sharedLink = await this.findOrFail(auth.user.id, id);
        if (sharedLink.type !== enum_1.SharedLinkType.Individual) {
            throw new common_1.BadRequestException('Invalid shared link type');
        }
        const existingAssetIds = new Set(sharedLink.assets.map((asset) => asset.id));
        const notPresentAssetIds = dto.assetIds.filter((assetId) => !existingAssetIds.has(assetId));
        const allowedAssetIds = await this.checkAccess({
            auth,
            permission: enum_1.Permission.AssetShare,
            ids: notPresentAssetIds,
        });
        const results = [];
        for (const assetId of dto.assetIds) {
            const hasAsset = existingAssetIds.has(assetId);
            if (hasAsset) {
                results.push({ assetId, success: false, error: asset_ids_response_dto_1.AssetIdErrorReason.DUPLICATE });
                continue;
            }
            const hasAccess = allowedAssetIds.has(assetId);
            if (!hasAccess) {
                results.push({ assetId, success: false, error: asset_ids_response_dto_1.AssetIdErrorReason.NO_PERMISSION });
                continue;
            }
            results.push({ assetId, success: true });
        }
        await this.sharedLinkRepository.update({
            ...sharedLink,
            assetIds: results.filter(({ success }) => success).map(({ assetId }) => assetId),
        });
        return results;
    }
    async removeAssets(auth, id, dto) {
        const sharedLink = await this.findOrFail(auth.user.id, id);
        if (sharedLink.type !== enum_1.SharedLinkType.Individual) {
            throw new common_1.BadRequestException('Invalid shared link type');
        }
        const removedAssetIds = await this.sharedLinkAssetRepository.remove(id, dto.assetIds);
        const results = [];
        for (const assetId of dto.assetIds) {
            const wasRemoved = removedAssetIds.find((id) => id === assetId);
            if (!wasRemoved) {
                results.push({ assetId, success: false, error: asset_ids_response_dto_1.AssetIdErrorReason.NOT_FOUND });
                continue;
            }
            results.push({ assetId, success: true });
            sharedLink.assets = sharedLink.assets.filter((asset) => asset.id !== assetId);
        }
        await this.sharedLinkRepository.update(sharedLink);
        return results;
    }
    async getMetadataTags(auth, defaultDomain) {
        if (!auth.sharedLink || auth.sharedLink.password) {
            return null;
        }
        const config = await this.getConfig({ withCache: true });
        const sharedLink = await this.findOrFail(auth.sharedLink.userId, auth.sharedLink.id);
        const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id;
        const assetCount = sharedLink.assets.length > 0 ? sharedLink.assets.length : sharedLink.album?.assets?.length || 0;
        const imagePath = assetId
            ? `/api/assets/${assetId}/thumbnail?key=${sharedLink.key.toString('base64url')}`
            : '/feature-panel.png';
        return {
            title: sharedLink.album ? sharedLink.album.albumName : 'Public Share',
            description: sharedLink.description || `${assetCount} shared photos & videos`,
            imageUrl: new URL(imagePath, (0, misc_1.getExternalDomain)(config.server, defaultDomain)).href,
        };
    }
    asToken(sharedLink) {
        return this.cryptoRepository.hashSha256(`${sharedLink.id}-${sharedLink.password}`).toString('base64');
    }
};
exports.SharedLinkService = SharedLinkService;
exports.SharedLinkService = SharedLinkService = __decorate([
    (0, common_1.Injectable)()
], SharedLinkService);
//# sourceMappingURL=shared-link.service.js.map