4 * Import failed domains from WHMCS with today's date as expiration
5 * Handles domains that had null/invalid expiration dates
8require('dotenv').config();
9const fs = require('fs');
10const path = require('path');
11const pool = require('../services/db');
13const EXPORT_DIR = path.join(__dirname, '../exports');
15// Get tenant ID from Precise PCs tenant file
19function getTenantId() {
20 const tenantFilePath = path.join(EXPORT_DIR, 'precise_pcs_tenant.json');
22 if (fs.existsSync(tenantFilePath)) {
24 const tenantData = JSON.parse(fs.readFileSync(tenantFilePath, 'utf8'));
25 console.log(`📂 Using tenant: ${tenantData.name} (${tenantData.tenant_id})`);
26 return tenantData.tenant_id;
28 console.warn('⚠️ Could not read tenant file, using default');
32 return process.env.TENANT_ID || '00000000-0000-0000-0000-000000000001';
36 * Map WHMCS status to our domain status
39function mapDomainStatus(whmcsStatus) {
43 'Pending Transfer': 'pending_transfer',
45 'Cancelled': 'cancelled',
47 'Transferred Away': 'transferred_out',
48 'Redemption': 'redemption'
51 return statusMap[whmcsStatus] || 'unknown';
55 * Parse date from WHMCS format
58function parseDate(dateString) {
59 if (!dateString || dateString === 'Invalid Date') {
62 const date = new Date(dateString);
63 return isNaN(date.getTime()) ? null : date.toISOString();
67 * Create customer lookup map from WHMCS user ID to our customer ID
69async function createCustomerLookup() {
70 const importLogPath = path.join(EXPORT_DIR, 'customer_import_log.json');
72 if (fs.existsSync(importLogPath)) {
73 const log = JSON.parse(fs.readFileSync(importLogPath, 'utf8'));
76 const entries = Array.isArray(log) ? log : (log.imported || []);
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;
86 console.log(`📋 Customer lookup: ${Object.keys(lookup).length} mappings loaded`);
94 * Import failed domains with today's date as expiration
96async function importFailedDomains() {
97 const tenantId = getTenantId();
100 console.log('========================================');
101 console.log('Import Failed Domains (with today\'s date)');
102 console.log('========================================\n');
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.');
110 const importLog = JSON.parse(fs.readFileSync(logPath, 'utf8'));
111 const failedDomains = importLog.errors || [];
113 console.log(`Found ${failedDomains.length} failed domains to retry\n`);
115 if (failedDomains.length === 0) {
116 console.log('No failed domains to import!');
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 || [];
125 // Create customer lookup
126 const customerLookup = await createCustomerLookup();
128 // Create a map of failed domain names for quick lookup
129 const failedDomainNames = new Set(failedDomains.map(d => d.domain?.toLowerCase()));
131 console.log('Retrying failed domains with today\'s date as expiration...\n');
138 imported_at: new Date().toISOString(),
144 // Today's date for expiration
145 const todayDate = new Date().toISOString();
147 for (const domain of allDomains) {
149 // Only process domains that failed before
150 if (!domain.domain || !failedDomainNames.has(domain.domain.toLowerCase())) {
155 const customerId = customerLookup[domain.userid];
157 console.log(`⚠️ Skipping ${domain.domain}: Customer not found (WHMCS user ${domain.userid})`);
159 retryLog.skipped.push({
161 domain: domain.domain,
162 whmcs_user_id: domain.userid,
163 reason: 'Customer not found'
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()]
174 if (existing.rows.length > 0) {
175 console.log(`⏭️ Skipping ${domain.domain}: Already exists`);
177 retryLog.skipped.push({
179 domain: domain.domain,
180 reason: 'Already exists'
185 // Prepare domain data - use today's date for expiration
186 const registrationDate = parseDate(domain.registrationdate) || todayDate;
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,
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'}`
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
211 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, NOW(), NOW()
213 RETURNING domain_id, domain_name, status
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,
223 domainData.registration_date,
224 domainData.expiration_date,
225 domainData.auto_renew,
226 domainData.whois_privacy,
227 domainData.renewal_price,
232 const insertedDomain = result.rows[0];
234 console.log(`✅ Imported: ${insertedDomain.domain_name} (${insertedDomain.status}) - Expiry set to: ${new Date(todayDate).toLocaleDateString()}`);
237 retryLog.imported.push({
238 domain_id: insertedDomain.domain_id,
239 domain_name: insertedDomain.domain_name,
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)'
250 console.error(`❌ Error importing ${domain.domain}:`, error.message);
252 retryLog.errors.push({
254 domain: domain.domain,
261 const retryLogPath = path.join(EXPORT_DIR, 'domain_retry_import_log.json');
262 fs.writeFileSync(retryLogPath, JSON.stringify(retryLog, null, 2));
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');
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');
279 console.error('❌ Failed domain import failed:', error.message);
286// Run if executed directly
287if (require.main === module) {
288 importFailedDomains()
290 console.log('✅ Failed domain retry complete');
294 console.error('❌ Failed domain retry failed:', error);
299module.exports = { importFailedDomains };