"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadPublicApiVersions = void 0;
exports.isApiEnabled = isApiEnabled;
const config_1 = require("@n8n/config");
const di_1 = require("@n8n/di");
const express_1 = __importDefault(require("express"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const validator_1 = __importDefault(require("validator"));
const backend_common_1 = require("@n8n/backend-common");
const event_service_1 = require("../events/event.service");
const license_1 = require("../license");
const api_key_auth_strategy_1 = require("../services/api-key-auth.strategy");
const auth_strategy_registry_1 = require("../services/auth-strategy.registry");
const last_active_at_service_1 = require("../services/last-active-at.service");
const url_service_1 = require("../services/url.service");
const public_api_error_response_1 = require("./v1/public-api-error-response");
function createLazySwaggerMiddleware(openApiSpecPath, publicApiEndpoint, version) {
    let cachedRouter;
    return async (req, res, next) => {
        if (!cachedRouter) {
            const globalConfig = di_1.Container.get(config_1.GlobalConfig);
            const n8nPath = globalConfig.path;
            const YAML = await Promise.resolve().then(() => __importStar(require('yaml')));
            const spec = await promises_1.default.readFile(openApiSpecPath, 'utf-8');
            const swaggerDocument = YAML.parse(spec);
            swaggerDocument.server = [
                {
                    url: `${di_1.Container.get(url_service_1.UrlService).getInstanceBaseUrl()}/${publicApiEndpoint}/${version}}`,
                },
            ];
            const { serveFiles, setup } = await Promise.resolve().then(() => __importStar(require('swagger-ui-express')));
            const swaggerThemePath = path_1.default.join(__dirname, 'swagger-theme.css');
            const swaggerThemeCss = await promises_1.default.readFile(swaggerThemePath, { encoding: 'utf-8' });
            cachedRouter = express_1.default.Router();
            cachedRouter.use(serveFiles(swaggerDocument), setup(swaggerDocument, {
                customCss: swaggerThemeCss,
                customSiteTitle: 'n8n Public API UI',
                customfavIcon: `${n8nPath}favicon.ico`,
            }));
        }
        void cachedRouter(req, res, next);
    };
}
function createLazyValidatorMiddleware(openApiSpecPath, handlersDirectory, version) {
    let cachedRouter;
    let initPromise;
    return async (req, res, next) => {
        if (!cachedRouter) {
            initPromise ??= (async () => {
                const { middleware: openApiValidatorMiddleware } = await Promise.resolve().then(() => __importStar(require('express-openapi-validator')));
                const authenticate = async (req) => {
                    const authenticated = await di_1.Container.get(auth_strategy_registry_1.AuthStrategyRegistry).authenticate(req);
                    if (authenticated) {
                        di_1.Container.get(last_active_at_service_1.LastActiveAtService)
                            .updateLastActiveIfStale(req.user.id)
                            .catch((error) => {
                            di_1.Container.get(backend_common_1.Logger).error('Failed to update last active timestamp', {
                                error,
                            });
                        });
                        di_1.Container.get(event_service_1.EventService).emit('public-api-invoked', {
                            userId: req.user.id,
                            path: req.path,
                            method: req.method,
                            apiVersion: version,
                            userAgent: req.headers['user-agent'],
                        });
                    }
                    return authenticated;
                };
                const router = express_1.default.Router();
                router.use(openApiValidatorMiddleware({
                    apiSpec: openApiSpecPath,
                    operationHandlers: handlersDirectory,
                    validateRequests: true,
                    validateApiSpec: true,
                    formats: {
                        email: {
                            type: 'string',
                            validate: (email) => validator_1.default.isEmail(email),
                        },
                        identifier: {
                            type: 'string',
                            validate: (identifier) => validator_1.default.isUUID(identifier) || validator_1.default.isEmail(identifier),
                        },
                        jsonString: {
                            validate: (data) => {
                                try {
                                    JSON.parse(data);
                                    return true;
                                }
                                catch (e) {
                                    return false;
                                }
                            },
                        },
                        nanoid: {
                            type: 'string',
                            validate: (id) => {
                                return /^[A-Za-z0-9]{16}$/.test(id);
                            },
                        },
                    },
                    validateSecurity: {
                        handlers: {
                            ApiKeyAuth: authenticate,
                            BearerAuth: authenticate,
                        },
                    },
                }));
                return router;
            })();
            cachedRouter = await initPromise;
        }
        void cachedRouter(req, res, next);
    };
}
function createApiRouter(version, openApiSpecPath, handlersDirectory, publicApiEndpoint) {
    const globalConfig = di_1.Container.get(config_1.GlobalConfig);
    const apiController = express_1.default.Router();
    if (!globalConfig.publicApi.swaggerUiDisabled) {
        apiController.use(`/${publicApiEndpoint}/${version}/docs`, createLazySwaggerMiddleware(openApiSpecPath, publicApiEndpoint, version));
    }
    apiController.get(`/${publicApiEndpoint}/${version}/openapi.yml`, (_, res) => {
        res.sendFile(openApiSpecPath);
    });
    const jsonParseErrorHandler = (error, _req, res, next) => {
        if (error instanceof SyntaxError && 'body' in error) {
            res.status(400).json({
                message: 'Invalid JSON in request body',
            });
            return;
        }
        next(error);
    };
    apiController.use(`/${publicApiEndpoint}/${version}`, express_1.default.json(), jsonParseErrorHandler, createLazyValidatorMiddleware(openApiSpecPath, handlersDirectory, version));
    const publicApiErrorHandler = (error, _req, res, _next) => {
        (0, public_api_error_response_1.sendPublicApiErrorResponse)(res, error);
    };
    apiController.use(publicApiErrorHandler);
    return apiController;
}
const loadPublicApiVersions = async (publicApiEndpoint) => {
    const registry = di_1.Container.get(auth_strategy_registry_1.AuthStrategyRegistry);
    registry.register(di_1.Container.get(api_key_auth_strategy_1.ApiKeyAuthStrategy));
    const folders = await promises_1.default.readdir(__dirname);
    const versions = folders.filter((folderName) => folderName.startsWith('v'));
    const apiRouters = versions.map((version) => {
        const openApiPath = path_1.default.join(__dirname, version, 'openapi.yml');
        return createApiRouter(version, openApiPath, __dirname, publicApiEndpoint);
    });
    const version = versions.pop()?.charAt(1);
    return {
        apiRouters,
        apiLatestVersion: version ? Number(version) : 1,
    };
};
exports.loadPublicApiVersions = loadPublicApiVersions;
function isApiEnabled() {
    return !di_1.Container.get(config_1.GlobalConfig).publicApi.disabled && !di_1.Container.get(license_1.License).isAPIDisabled();
}
//# sourceMappingURL=index.js.map