EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
import_failed_domains.js
Go to the documentation of this file.
1#!/usr/bin/env node
2
3/**
4 * Import failed domains from WHMCS with today's date as expiration
5 * Handles domains that had null/invalid expiration dates
6 */
7
8require('dotenv').config();
9const fs = require('fs');
10const path = require('path');
11const pool = require('../services/db');
12
13const EXPORT_DIR = path.join(__dirname, '../exports');
14
15// Get tenant ID from Precise PCs tenant file
16/**
17 *
18 */
19function getTenantId() {
20 const tenantFilePath = path.join(EXPORT_DIR, 'precise_pcs_tenant.json');
21
22 if (fs.existsSync(tenantFilePath)) {
23 try {
24 const tenantData = JSON.parse(fs.readFileSync(tenantFilePath, 'utf8'));
25 console.log(`📂 Using tenant: ${tenantData.name} (${tenantData.tenant_id})`);
26 return tenantData.tenant_id;
27 } catch (error) {
28 console.warn('⚠️ Could not read tenant file, using default');
29 }
30 }
31
32 return process.env.TENANT_ID || '00000000-0000-0000-0000-000000000001';
33}
34
35/**
36 * Map WHMCS status to our domain status
37 * @param whmcsStatus
38 */
39function mapDomainStatus(whmcsStatus) {
40 const statusMap = {
41 'Active': 'active',
42 'Pending': 'pending',
43 'Pending Transfer': 'pending_transfer',
44 'Expired': 'expired',
45 'Cancelled': 'cancelled',
46 'Fraud': 'cancelled',
47 'Transferred Away': 'transferred_out',
48 'Redemption': 'redemption'
49 };
50
51 return statusMap[whmcsStatus] || 'unknown';
52}
53
54/**
55 * Parse date from WHMCS format
56 * @param dateString
57 */
58function parseDate(dateString) {
59 if (!dateString || dateString === 'Invalid Date') {
60 return null;
61 }
62 const date = new Date(dateString);
63 return isNaN(date.getTime()) ? null : date.toISOString();
64}
65
66/**
67 * Create customer lookup map from WHMCS user ID to our customer ID
68 */
69async function createCustomerLookup() {
70 const importLogPath = path.join(EXPORT_DIR, 'customer_import_log.json');
71
72 if (fs.existsSync(importLogPath)) {
73 const log = JSON.parse(fs.readFileSync(importLogPath, 'utf8'));
74 const lookup = {};
75
76 const entries = Array.isArray(log) ? log : (log.imported || []);
77
78 for (const entry of entries) {
79 if (entry.whmcs_id && entry.postgres_id) {
80 lookup[entry.whmcs_id] = entry.postgres_id;
81 } else if (entry.whmcs_id && entry.customer_id) {
82 lookup[entry.whmcs_id] = entry.customer_id;
83 }
84 }
85
86 console.log(`📋 Customer lookup: ${Object.keys(lookup).length} mappings loaded`);
87 return lookup;
88 }
89
90 return {};
91}
92
93/**
94 * Import failed domains with today's date as expiration
95 */
96async function importFailedDomains() {
97 const tenantId = getTenantId();
98
99 try {
100 console.log('========================================');
101 console.log('Import Failed Domains (with today\'s date)');
102 console.log('========================================\n');
103
104 // Load the domain import log to get failed domains
105 const logPath = path.join(EXPORT_DIR, 'domain_import_log.json');
106 if (!fs.existsSync(logPath)) {
107 throw new Error('Domain import log not found. Run import_whmcs_domains.js first.');
108 }
109
110 const importLog = JSON.parse(fs.readFileSync(logPath, 'utf8'));
111 const failedDomains = importLog.errors || [];
112
113 console.log(`Found ${failedDomains.length} failed domains to retry\n`);
114
115 if (failedDomains.length === 0) {
116 console.log('No failed domains to import!');
117 return;
118 }
119
120 // Load full WHMCS export data
121 const jsonPath = path.join(EXPORT_DIR, 'whmcs_export.json');
122 const exportData = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
123 const allDomains = exportData.domains || [];
124
125 // Create customer lookup
126 const customerLookup = await createCustomerLookup();
127
128 // Create a map of failed domain names for quick lookup
129 const failedDomainNames = new Set(failedDomains.map(d => d.domain?.toLowerCase()));
130
131 console.log('Retrying failed domains with today\'s date as expiration...\n');
132
133 let imported = 0;
134 let skipped = 0;
135 let errors = 0;
136 const retryLog = {
137 tenant_id: tenantId,
138 imported_at: new Date().toISOString(),
139 imported: [],
140 skipped: [],
141 errors: []
142 };
143
144 // Today's date for expiration
145 const todayDate = new Date().toISOString();
146
147 for (const domain of allDomains) {
148 try {
149 // Only process domains that failed before
150 if (!domain.domain || !failedDomainNames.has(domain.domain.toLowerCase())) {
151 continue;
152 }
153
154 // Get customer ID
155 const customerId = customerLookup[domain.userid];
156 if (!customerId) {
157 console.log(`⚠️ Skipping ${domain.domain}: Customer not found (WHMCS user ${domain.userid})`);
158 skipped++;
159 retryLog.skipped.push({
160 whmcs_id: domain.id,
161 domain: domain.domain,
162 whmcs_user_id: domain.userid,
163 reason: 'Customer not found'
164 });
165 continue;
166 }
167
168 // Check if domain was already imported successfully
169 const existing = await pool.query(
170 'SELECT domain_id FROM domains WHERE tenant_id = $1 AND domain_name = $2',
171 [tenantId, domain.domain.toLowerCase()]
172 );
173
174 if (existing.rows.length > 0) {
175 console.log(`⏭️ Skipping ${domain.domain}: Already exists`);
176 skipped++;
177 retryLog.skipped.push({
178 whmcs_id: domain.id,
179 domain: domain.domain,
180 reason: 'Already exists'
181 });
182 continue;
183 }
184
185 // Prepare domain data - use today's date for expiration
186 const registrationDate = parseDate(domain.registrationdate) || todayDate;
187
188 const domainData = {
189 tenant_id: tenantId,
190 customer_id: customerId,
191 domain_name: domain.domain.toLowerCase(),
192 registrar: domain.registrar || 'unknown',
193 registrar_domain_id: domain.id.toString(),
194 status: mapDomainStatus(domain.status),
195 registration_date: registrationDate,
196 expiration_date: todayDate, // Use today's date since original was null
197 auto_renew: domain.donotrenew ? false : true,
198 whois_privacy: domain.idprotection ? true : false,
199 renewal_price: parseFloat(domain.recurringamount) || 0,
200 currency: 'AUD',
201 notes: `Migrated from WHMCS (expiry date missing - set to import date)\nWHMCS Domain ID: ${domain.id}\nOriginal Status: ${domain.status}\nRegistration Type: ${domain.type || 'Unknown'}\nPayment Method: ${domain.paymentmethod || 'N/A'}`
202 };
203
204 // Insert domain
205 const insertQuery = `
206 INSERT INTO domains (
207 tenant_id, customer_id, domain_name, registrar, registrar_domain_id,
208 status, registration_date, expiration_date, auto_renew, whois_privacy,
209 renewal_price, currency, notes, created_at, updated_at
210 ) VALUES (
211 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, NOW(), NOW()
212 )
213 RETURNING domain_id, domain_name, status
214 `;
215
216 const result = await pool.query(insertQuery, [
217 domainData.tenant_id,
218 domainData.customer_id,
219 domainData.domain_name,
220 domainData.registrar,
221 domainData.registrar_domain_id,
222 domainData.status,
223 domainData.registration_date,
224 domainData.expiration_date,
225 domainData.auto_renew,
226 domainData.whois_privacy,
227 domainData.renewal_price,
228 domainData.currency,
229 domainData.notes
230 ]);
231
232 const insertedDomain = result.rows[0];
233
234 console.log(`✅ Imported: ${insertedDomain.domain_name} (${insertedDomain.status}) - Expiry set to: ${new Date(todayDate).toLocaleDateString()}`);
235
236 imported++;
237 retryLog.imported.push({
238 domain_id: insertedDomain.domain_id,
239 domain_name: insertedDomain.domain_name,
240 whmcs_id: domain.id,
241 whmcs_user_id: domain.userid,
242 customer_id: customerId,
243 status: insertedDomain.status,
244 registrar: domainData.registrar,
245 expiration_date: domainData.expiration_date,
246 note: 'Expiration date set to import date (was null in WHMCS)'
247 });
248
249 } catch (error) {
250 console.error(`❌ Error importing ${domain.domain}:`, error.message);
251 errors++;
252 retryLog.errors.push({
253 whmcs_id: domain.id,
254 domain: domain.domain,
255 error: error.message
256 });
257 }
258 }
259
260 // Save retry log
261 const retryLogPath = path.join(EXPORT_DIR, 'domain_retry_import_log.json');
262 fs.writeFileSync(retryLogPath, JSON.stringify(retryLog, null, 2));
263
264 console.log('\n========================================');
265 console.log('Failed Domain Retry Summary');
266 console.log('========================================');
267 console.log(`✅ Imported: ${imported}`);
268 console.log(`⏭️ Skipped: ${skipped}`);
269 console.log(`❌ Errors: ${errors}`);
270 console.log('\nRetry log saved:', retryLogPath);
271 console.log('========================================\n');
272
273 if (imported > 0) {
274 console.log('⚠️ Note: All imported domains have expiration date set to TODAY.');
275 console.log(' Please update expiration dates manually if you have the correct dates.\n');
276 }
277
278 } catch (error) {
279 console.error('❌ Failed domain import failed:', error.message);
280 throw error;
281 } finally {
282 await pool.end();
283 }
284}
285
286// Run if executed directly
287if (require.main === module) {
288 importFailedDomains()
289 .then(() => {
290 console.log('✅ Failed domain retry complete');
291 process.exit(0);
292 })
293 .catch((error) => {
294 console.error('❌ Failed domain retry failed:', error);
295 process.exit(1);
296 });
297}
298
299module.exports = { importFailedDomains };