@gohighlevel/api-client
Version: 
Official SDK for HighLevel Public APIs
163 lines • 6.99 kB
JavaScript
;
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;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebhookManager = void 0;
const crypto = __importStar(require("crypto"));
/**
 * WebhookManager handles incoming webhooks from GoHighLevel
 * Provides Express middleware for processing webhook events
 */
class WebhookManager {
    constructor(logger, sessionStorage, oauthService) {
        this.logger = logger;
        this.sessionStorage = sessionStorage;
        this.oauthService = oauthService;
    }
    /**
     * Returns Express middleware for handling GoHighLevel webhooks
     * For all webhooks, it will validate the webhook signature if received.
     * This middleware will handle INSTALL and UNINSTALL webhooks.
     * It will automatically generate token and store it for INSTALL webhook event
     * It will automatically remove token for UNINSTALL webhook event
     */
    subscribe() {
        return async (req, res, next) => {
            this.logger.debug('Webhook received', {
                method: req.method,
                url: req.url,
                headers: req.headers,
                body: req.body,
            });
            try {
                const clientId = process.env.CLIENT_ID;
                const appId = clientId ? clientId.split('-')[0] : '';
                if (appId !== req.body.appId) {
                    this.logger.warn('App ID mismatch, skipping webhook processing');
                    next();
                }
                // Initialize request flags
                req.skippedSignatureVerification = false;
                req.isSignatureValid = false;
                // Verify webhook signature
                const signature = req.headers['x-wh-signature'];
                const publicKey = process.env.WEBHOOK_PUBLIC_KEY;
                if (signature && publicKey) {
                    const payload = JSON.stringify(req.body);
                    const isValid = this.verifySignature(payload, signature, publicKey);
                    req.isSignatureValid = isValid;
                    if (!isValid) {
                        this.logger.warn('Invalid webhook signature');
                        next();
                    }
                }
                else {
                    this.logger.warn('Skipping signature verification - missing signature or public key');
                    req.skippedSignatureVerification = true;
                }
                const requestBody = req.body;
                const companyId = requestBody.companyId;
                const locationId = requestBody.locationId;
                switch (requestBody.type) {
                    case 'INSTALL':
                        if (companyId && locationId) {
                            this.generateLocationAccessToken(companyId, locationId);
                        }
                        break;
                    case 'UNINSTALL':
                        if (locationId || companyId) {
                            const resourceId = locationId || companyId;
                            this.sessionStorage.deleteSession(resourceId);
                        }
                        break;
                }
                this.logger.debug('Webhook processed successfully');
                next();
            }
            catch (error) {
                this.logger.error('Webhook processing failed:', error);
                next(error);
            }
        };
    }
    /**
     * Verify webhook signature using GoHighLevel's public key
     * @param payload - The JSON stringified request body
     * @param signature - The signature from x-wh-signature header
     * @param publicKey - The public key from environment variable
     * @returns True if signature is valid, false otherwise
     */
    verifySignature(payload, signature, publicKey) {
        try {
            this.logger.debug('Verifying webhook signature');
            const verifier = crypto.createVerify('sha256');
            verifier.update(payload);
            verifier.end();
            return verifier.verify(publicKey, signature, 'base64');
        }
        catch (error) {
            this.logger.error('Error verifying webhook signature:', error);
            return false;
        }
    }
    /**
     * Generate location access token and store it using company token
     * @param companyId - The company ID
     * @param locationId - The location ID
     */
    async generateLocationAccessToken(companyId, locationId) {
        try {
            // Get the token for the company from the store
            const companyToken = await this.sessionStorage.getAccessToken(companyId);
            if (!companyToken) {
                this.logger.warn(`Company token not found for companyId: ${companyId}, skipping location access token generation`);
                return;
            }
            this.logger.debug(`Generating location access token for location: ${locationId}`);
            // Get location access token using OAuth service
            const locationTokenResponse = await this.oauthService.getLocationAccessToken({
                companyId,
                locationId,
            });
            // Store the location token in session storage
            await this.sessionStorage.setSession(locationId, locationTokenResponse);
            this.logger.debug(`Location access token generated and stored for location: ${locationId}`);
        }
        catch (error) {
            this.logger.error(`Failed to generate location access token:`, error);
        }
    }
}
exports.WebhookManager = WebhookManager;
//# sourceMappingURL=webhook-manager.js.map