const { FreepbxGqlClient, gql } = require("freepbx-graphql-client"); class FreepbxManager { /** * Creates an instance of the FreepbxGqlClient and initializes the connection pool. * * @param {Object} config - Configuration object for the FreepbxGqlClient. * @param {string} config.url - The URL of the FreePBX GraphQL endpoint. * @param {string} config.clientId - The client ID for authentication. * @param {string} config.clientSecret - The client secret for authentication. * @param {Object} config.dbPool - The connection pool for managing database connections. */ constructor(config) { this.client = new FreepbxGqlClient(config.url, { client: { id: config.clientId, secret: config.clientSecret, }, }); this.pool = config.dbPool; if (!this.pool) { throw new Error("Connection pool is required"); } } async getExtension(ext) { ext = String(ext); const query = gql` query fetchExtension($extensionId: ID!) { fetchExtension(extensionId: $extensionId) { user { extension name extPassword voicemail } } } `; const variables = { extensionId: ext.match(/\d+/)[0], }; return await this.client.request(query, variables); } async listExtensions() { const query = gql` query { fetchAllExtensions { extension { user { extension name } } } } `; return await this.client.request(query); } async addExtension(ext, name) { ext = String(ext); name = String(name); const query = gql` mutation addExtension($ext: ID!, $name: String!, $vmPassword: String!) { addExtension(input: { extensionId: $ext name: $name vmEnable: true vmPassword: $vmPassword email: "" maxContacts: "100" umEnable: false }) { status } } `; const variables = { ext, name, vmPassword: ext, }; return await this.client.request(query, variables); } async deleteExtension(ext) { ext = String(ext); const query = gql` mutation deleteExtension($ext: ID!) { deleteExtension(input: { extensionId: $ext }) { status } } `; const variables = { ext, }; const fpbxQuery = this.client.request(query, variables); const dbQuery = this.pool.query('DELETE FROM paging_groups WHERE ext = ?', [ext]); return await Promise.all([fpbxQuery, dbQuery]); } async reload() { const query = gql` mutation { doreload(input: { clientMutationId: "${Math.random().toString(36).substring(2, 14)}" }) { status } } `; return await this.client.request(query); } // async updateName(ext, name) { // const query = gql` // mutation updateName($ext: ID!, $name: String!) { // updateExtension(input: {extensionId: $ext, name: $name}) { // status, // message // } // }`; // const variables = { // ext, // name, // }; // return await this.client.request(query, variables); // } // TODO: Implement updateName method, Current implementation resets extension for some reason async joinPageGroup(ext, pageGroup) { const [lookup] = await this.pool.query('SELECT * FROM paging_groups WHERE page_number = ? AND ext = ?', [pageGroup, ext]); if (lookup) { return false; } await this.pool.query('INSERT INTO paging_groups (page_number, ext) VALUES (?, ?)', [pageGroup, ext]); return true; }; async leavePageGroup(ext, pageGroup) { const [lookup] = await this.pool.query('SELECT * FROM paging_groups WHERE page_number = ? AND ext = ?', [pageGroup, ext]); if (!lookup) { return false; } await this.pool.query('DELETE FROM paging_groups WHERE page_number = ? AND ext = ?', [pageGroup, ext]); return true; }; async getNextAvailableExtension() { const extList = await this.listExtensions(); const exts = extList.fetchAllExtensions.extension; const startExt = process.env.START_EXT ? parseInt(process.env.START_EXT, 10) : 1000; const existingExts = exts.map(ext => parseInt(ext.user.extension, 10)).sort((a, b) => a - b); let nextExt = startExt; for (let i = 0; i < existingExts.length; i++) { if (existingExts[i] !== nextExt) { break; } nextExt++; } return nextExt; } } module.exports = FreepbxManager;