EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
whmcsMysqlService.js
Go to the documentation of this file.
1/**
2 * WHMCS MySQL Direct Access Service
3 * Queries WHMCS database directly for migration
4 */
5
6const mysql = require('mysql2/promise');
7
8/**
9 * WHMCS MySQL direct access service.
10 * Provides methods to query WHMCS database for migration purposes.
11 */
12class WHMCSMysqlService {
13 /**
14 * Initialize WHMCS MySQL service.
15 * Creates connection pool with credentials from environment variables.
16 */
17 constructor() {
18 this.config = {
19 host: process.env.whmcs_mysql_host,
20 port: parseInt(process.env.whmcs_mysql_port || '3306'),
21 user: process.env.whmcs_mysql_user,
22 password: process.env.whmcs_mysql_password,
23 database: process.env.whmcs_mysql_db,
24 waitForConnections: true,
25 connectionLimit: 10,
26 queueLimit: 0
27 };
28
29 if (!this.config.host || !this.config.user || !this.config.database) {
30 throw new Error('WHMCS MySQL credentials not configured in .env');
31 }
32
33 this.pool = null;
34 }
35
36 /**
37 * Get connection pool.
38 * Creates pool on first call and reuses for subsequent requests.
39 * @returns {Promise<mysql.Pool>} MySQL connection pool
40 */
41 async getPool() {
42 if (!this.pool) {
43 this.pool = mysql.createPool(this.config);
44 }
45 return this.pool;
46 }
47
48 /**
49 * Execute query.
50 * @param {string} sql - SQL query string
51 * @param {Array} [params] - Query parameters
52 * @returns {Promise<Array>} Query results
53 */
54 async query(sql, params = []) {
55 const pool = await this.getPool();
56 const [rows] = await pool.execute(sql, params);
57 return rows;
58 }
59
60 /**
61 * Test database connection.
62 * @returns {Promise<boolean>} True if connection successful
63 */
64 async testConnection() {
65 try {
66 const pool = await this.getPool();
67 await pool.query('SELECT 1');
68 console.log('[WHMCS MySQL] ✅ Database connection successful');
69 return true;
70 } catch (error) {
71 console.error('[WHMCS MySQL] ❌ Database connection failed:', error.message);
72 return false;
73 }
74 }
75
76 /**
77 * Get all clients (customers).
78 * @param {string} [status] - Filter by status (e.g., 'Active')
79 * @returns {Promise<Array>} Client records
80 */
81 async getClients(status = 'Active') {
82 const sql = status
83 ? 'SELECT * FROM tblclients WHERE status = ? ORDER BY id'
84 : 'SELECT * FROM tblclients ORDER BY id';
85
86 return await this.query(sql, status ? [status] : []);
87 }
88
89 /**
90 * Get client by ID.
91 * @param {number} clientId - Client ID
92 * @returns {Promise<object|null>} Client record or null
93 */
94 async getClientById(clientId) {
95 const sql = 'SELECT * FROM tblclients WHERE id = ?';
96 const rows = await this.query(sql, [clientId]);
97 return rows[0] || null;
98 }
99
100 /**
101 * Get all domains.
102 * @returns {Promise<Array>} Domain records with client information
103 */
104 async getDomains() {
105 const sql = `
106 SELECT
107 d.*,
108 c.firstname, c.lastname, c.email, c.companyname
109 FROM tbldomains d
110 LEFT JOIN tblclients c ON d.userid = c.id
111 ORDER BY d.id
112 `;
113 return await this.query(sql);
114 }
115
116 /**
117 * Get domains for specific client.
118 * @param {number} clientId - Client ID
119 * @returns {Promise<Array>} Domain records
120 */
121 async getDomainsByClient(clientId) {
122 const sql = 'SELECT * FROM tbldomains WHERE userid = ? ORDER BY id';
123 return await this.query(sql, [clientId]);
124 }
125
126 /**
127 * Get all hosting/products.
128 * @returns {Promise<Array>} Product records with client and product information
129 */
130 async getProducts() {
131 const sql = `
132 SELECT
133 h.*,
134 c.firstname, c.lastname, c.email, c.companyname,
135 p.name as product_name, p.type as product_type
136 FROM tblhosting h
137 LEFT JOIN tblclients c ON h.userid = c.id
138 LEFT JOIN tblproducts p ON h.packageid = p.id
139 ORDER BY h.id
140 `;
141 return await this.query(sql);
142 }
143
144 /**
145 * Get products for specific client.
146 * @param {number} clientId - Client ID
147 * @returns {Promise<Array>} Product records with product information
148 */
149 async getProductsByClient(clientId) {
150 const sql = `
151 SELECT
152 h.*,
153 p.name as product_name, p.type as product_type
154 FROM tblhosting h
155 LEFT JOIN tblproducts p ON h.packageid = p.id
156 WHERE h.userid = ?
157 ORDER BY h.id
158 `;
159 return await this.query(sql, [clientId]);
160 }
161
162 /**
163 * Get all invoices.
164 * @returns {Promise<Array>} Invoice records with client information
165 */
166 async getInvoices() {
167 const sql = `
168 SELECT
169 i.*,
170 c.firstname, c.lastname, c.email, c.companyname
171 FROM tblinvoices i
172 LEFT JOIN tblclients c ON i.userid = c.id
173 ORDER BY i.id
174 `;
175 return await this.query(sql);
176 }
177
178 /**
179 * Get invoice items.
180 * @param {number} invoiceId - Invoice ID
181 * @returns {Promise<Array>} Invoice item records
182 */
183 async getInvoiceItems(invoiceId) {
184 const sql = 'SELECT * FROM tblinvoiceitems WHERE invoiceid = ? ORDER BY id';
185 return await this.query(sql, [invoiceId]);
186 }
187
188 /**
189 * Get all tickets.
190 * @param {string|null} [status] - Filter by status
191 * @returns {Promise<Array>} Ticket records
192 */
193 async getTickets(status = null) {
194 const sql = status
195 ? 'SELECT * FROM tbltickets WHERE status = ? ORDER BY id'
196 : 'SELECT * FROM tbltickets ORDER BY id';
197
198 return await this.query(sql, status ? [status] : []);
199 }
200
201 /**
202 * Get ticket replies.
203 * @param {number} ticketId - Ticket ID
204 * @returns {Promise<Array>} Ticket reply records
205 */
206 async getTicketReplies(ticketId) {
207 const sql = 'SELECT * FROM tblticketreplies WHERE tid = ? ORDER BY id';
208 return await this.query(sql, [ticketId]);
209 }
210
211 /**
212 * Get custom fields.
213 * @returns {Promise<Array>} Custom field definitions
214 */
215 async getCustomFields() {
216 const sql = 'SELECT * FROM tblcustomfields WHERE type = "client" ORDER BY id';
217 return await this.query(sql);
218 }
219
220 /**
221 * Get custom field values for client.
222 * @param {number} clientId - Client ID
223 * @returns {Promise<Array>} Custom field value records
224 */
225 async getCustomFieldValues(clientId) {
226 const sql = `
227 SELECT
228 v.*,
229 f.fieldname, f.fieldtype
230 FROM tblcustomfieldsvalues v
231 LEFT JOIN tblcustomfields f ON v.fieldid = f.id
232 WHERE v.relid = ? AND f.type = 'client'
233 `;
234 return await this.query(sql, [clientId]);
235 }
236
237 /**
238 * Get database statistics.
239 * @returns {Promise<object>} Statistics with counts for all entities
240 */
241 async getStatistics() {
242 const stats = {};
243
244 stats.clients = await this.query('SELECT COUNT(*) as count FROM tblclients');
245 stats.activeClients = await this.query('SELECT COUNT(*) as count FROM tblclients WHERE status = "Active"');
246 stats.domains = await this.query('SELECT COUNT(*) as count FROM tbldomains');
247 stats.products = await this.query('SELECT COUNT(*) as count FROM tblhosting');
248 stats.invoices = await this.query('SELECT COUNT(*) as count FROM tblinvoices');
249 stats.tickets = await this.query('SELECT COUNT(*) as count FROM tbltickets');
250
251 return {
252 totalClients: stats.clients[0].count,
253 activeClients: stats.activeClients[0].count,
254 totalDomains: stats.domains[0].count,
255 totalProducts: stats.products[0].count,
256 totalInvoices: stats.invoices[0].count,
257 totalTickets: stats.tickets[0].count
258 };
259 }
260
261 /**
262 * Export all data for migration.
263 * @returns {Promise<object>} Complete export with clients, domains, products, etc.
264 */
265 async exportAllData() {
266 console.log('[WHMCS MySQL] Starting full data export...');
267
268 const stats = await this.getStatistics();
269 console.log('[WHMCS MySQL] Database Statistics:');
270 console.log(` - Total Clients: ${stats.totalClients}`);
271 console.log(` - Active Clients: ${stats.activeClients}`);
272 console.log(` - Domains: ${stats.totalDomains}`);
273 console.log(` - Products: ${stats.totalProducts}`);
274 console.log(` - Invoices: ${stats.totalInvoices}`);
275 console.log(` - Tickets: ${stats.totalTickets}\n`);
276
277 console.log('[WHMCS MySQL] Fetching clients...');
278 const clients = await this.getClients('Active');
279 console.log(`[WHMCS MySQL] Found ${clients.length} active clients`);
280
281 console.log('[WHMCS MySQL] Fetching domains...');
282 const domains = await this.getDomains();
283 console.log(`[WHMCS MySQL] Found ${domains.length} domains`);
284
285 console.log('[WHMCS MySQL] Fetching products...');
286 const products = await this.getProducts();
287 console.log(`[WHMCS MySQL] Found ${products.length} products`);
288
289 console.log('[WHMCS MySQL] Fetching custom fields...');
290 const customFields = await this.getCustomFields();
291 console.log(`[WHMCS MySQL] Found ${customFields.length} custom fields`);
292
293 return {
294 clients,
295 domains,
296 products,
297 customFields,
298 statistics: stats,
299 exportedAt: new Date().toISOString()
300 };
301 }
302
303 /**
304 * Close connection pool
305 */
306 async close() {
307 if (this.pool) {
308 await this.pool.end();
309 this.pool = null;
310 }
311 }
312}
313
314module.exports = WHMCSMysqlService;