"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;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OwnershipService = void 0;
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const backend_common_1 = require("@n8n/backend-common");
const cache_service_1 = require("../services/cache/cache.service");
const bad_request_error_1 = require("../errors/response-errors/bad-request.error");
const event_service_1 = require("../events/event.service");
const password_utility_1 = require("./password.utility");
const IsNull_1 = require("@n8n/typeorm/find-options/operator/IsNull");
const Not_1 = require("@n8n/typeorm/find-options/operator/Not");
const config_1 = __importDefault(require("../config"));
let OwnershipService = class OwnershipService {
    constructor(cacheService, eventService, logger, passwordUtility, projectRelationRepository, projectRepository, sharedWorkflowRepository, userRepository, settingsRepository) {
        this.cacheService = cacheService;
        this.eventService = eventService;
        this.logger = logger;
        this.passwordUtility = passwordUtility;
        this.projectRelationRepository = projectRelationRepository;
        this.projectRepository = projectRepository;
        this.sharedWorkflowRepository = sharedWorkflowRepository;
        this.userRepository = userRepository;
        this.settingsRepository = settingsRepository;
    }
    copyProject(project) {
        return {
            ...project,
        };
    }
    reconstructProject(project) {
        if (typeof project !== 'object' || project === null) {
            return undefined;
        }
        return Object.assign(new db_1.Project(), project);
    }
    copyUser(user) {
        return {
            ...user,
            role: { ...user.role, scopes: [...user.role.scopes] },
        };
    }
    reconstructUser(cachedUser) {
        if (typeof cachedUser !== 'object' || cachedUser === null) {
            return undefined;
        }
        const user = Object.assign(new db_1.User(), cachedUser);
        if ('role' in cachedUser && cachedUser.role && typeof cachedUser.role === 'object') {
            user.role = Object.assign(new db_1.Role(), cachedUser.role);
            if ('scopes' in cachedUser.role && Array.isArray(cachedUser.role.scopes)) {
                user.role.scopes = cachedUser.role.scopes.map((scope) => {
                    const x = Object.assign(new db_1.Scope(), scope);
                    return x;
                });
            }
            return user;
        }
        return undefined;
    }
    async getWorkflowProjectCached(workflowId) {
        const cachedValue = await this.cacheService.getHashValue('workflow-project', workflowId);
        if (cachedValue) {
            const project = this.reconstructProject(cachedValue);
            if (project)
                return project;
        }
        const sharedWorkflow = await this.sharedWorkflowRepository.findOneOrFail({
            where: { workflowId, role: 'workflow:owner' },
            relations: ['project'],
        });
        void this.cacheService.setHash('workflow-project', {
            [workflowId]: this.copyProject(sharedWorkflow.project),
        });
        return sharedWorkflow.project;
    }
    async setWorkflowProjectCacheEntry(workflowId, project) {
        void this.cacheService.setHash('workflow-project', {
            [workflowId]: this.copyProject(project),
        });
        return project;
    }
    async getPersonalProjectOwnerCached(projectId) {
        const cachedValue = await this.cacheService.getHashValue('project-owner', projectId);
        if (cachedValue) {
            const user = this.reconstructUser(cachedValue);
            if (user)
                return user;
        }
        const ownerRel = await this.projectRelationRepository.getPersonalProjectOwners([projectId]);
        const owner = ownerRel[0]?.user ?? null;
        if (owner) {
            void this.cacheService.setHash('project-owner', { [projectId]: this.copyUser(owner) });
        }
        return owner;
    }
    async invalidateProjectOwnerCacheByUserId(userId) {
        const personalProject = await this.projectRepository.getPersonalProjectForUser(userId);
        if (personalProject) {
            await this.cacheService.deleteFromHash('project-owner', personalProject.id);
        }
    }
    addOwnedByAndSharedWith(rawEntity) {
        const shared = rawEntity.shared;
        const entity = rawEntity;
        Object.assign(entity, {
            homeProject: null,
            sharedWithProjects: [],
        });
        if (shared === undefined) {
            return entity;
        }
        for (const sharedEntity of shared) {
            const { project, role } = sharedEntity;
            if (role === 'credential:owner' || role === 'workflow:owner') {
                entity.homeProject = {
                    id: project.id,
                    type: project.type,
                    name: project.name,
                    icon: project.icon,
                };
            }
            else {
                entity.sharedWithProjects.push({
                    id: project.id,
                    type: project.type,
                    name: project.name,
                    icon: project.icon,
                });
            }
        }
        return entity;
    }
    async getInstanceOwner() {
        return await this.userRepository.findOneOrFail({
            where: { role: { slug: db_1.GLOBAL_OWNER_ROLE.slug } },
        });
    }
    async hasInstanceOwner() {
        return await this.userRepository.exists({
            where: [
                {
                    role: { slug: db_1.GLOBAL_OWNER_ROLE.slug },
                    lastActiveAt: (0, Not_1.Not)((0, IsNull_1.IsNull)()),
                },
                {
                    role: { slug: db_1.GLOBAL_OWNER_ROLE.slug },
                    password: (0, Not_1.Not)((0, IsNull_1.IsNull)()),
                },
            ],
            relations: ['role'],
        });
    }
    async setupOwner(payload, options) {
        const { email, firstName, lastName, password } = payload;
        if (!options?.overwriteExisting && (await this.hasInstanceOwner())) {
            this.logger.debug('Request to claim instance ownership failed because instance owner already exists');
            throw new bad_request_error_1.BadRequestError('Instance owner already setup');
        }
        let shellUser = await this.userRepository.findOne({
            where: { role: { slug: db_1.GLOBAL_OWNER_ROLE.slug } },
            relations: ['role'],
        });
        if (!shellUser) {
            this.logger.error('Could not find shell user with global:owner role');
            throw new bad_request_error_1.BadRequestError('Instance owner shell user not found');
        }
        shellUser.email = email.toLowerCase();
        shellUser.firstName = firstName;
        shellUser.lastName = lastName;
        shellUser.lastActiveAt = new Date();
        shellUser.password = options?.passwordIsHashed
            ? password
            : await this.passwordUtility.hash(password);
        shellUser = await this.userRepository.save(shellUser, { transaction: false });
        this.logger.info('Owner was set up successfully');
        this.eventService.emit('instance-owner-setup', { userId: shellUser.id });
        await this.settingsRepository.update({ key: 'userManagement.isInstanceOwnerSetUp' }, { value: JSON.stringify(true) });
        config_1.default.set('userManagement.isInstanceOwnerSetUp', true);
        return shellUser;
    }
};
exports.OwnershipService = OwnershipService;
exports.OwnershipService = OwnershipService = __decorate([
    (0, di_1.Service)(),
    __metadata("design:paramtypes", [cache_service_1.CacheService,
        event_service_1.EventService,
        backend_common_1.Logger,
        password_utility_1.PasswordUtility,
        db_1.ProjectRelationRepository,
        db_1.ProjectRepository,
        db_1.SharedWorkflowRepository,
        db_1.UserRepository,
        db_1.SettingsRepository])
], OwnershipService);
//# sourceMappingURL=ownership.service.js.map