Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@ const eslintConfig = [
"react-hooks/set-state-in-effect": "warn",
"react-hooks/immutability": "warn",
"react-hooks/preserve-manual-memoization": "warn",
"no-console": ["error", { allow: ["warn", "error"] }],
},
},
{
files: ["scripts/**/*.js"],
files: ["scripts/**/*.{js,mjs}"],
rules: {
"@typescript-eslint/no-require-imports": "off",
"no-console": "off",
},
},
{
files: ["tests/**/*.{ts,js}"],
rules: {
"no-console": "off",
},
},
];
Expand Down
25 changes: 3 additions & 22 deletions src/app/api/adif/import/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,7 @@ async function parseAndImportADIF(content: string, userId: number, stationId: nu

// Parse records
const records = parseADIFRecords(dataContent);
console.log(`Parsed ${records.length} records from ADIF file`);
console.log(`First record sample:`, records[0]?.fields ? Object.keys(records[0].fields).slice(0, 5) : 'No records');


// Limit number of records for large imports using dynamic setting
if (records.length > maxRecords) {
return {
Expand Down Expand Up @@ -153,18 +151,10 @@ async function parseAndImportADIF(content: string, userId: number, stationId: nu
const startIdx = batchIndex * batchSize;
const endIdx = Math.min(startIdx + batchSize, records.length);
const batch = records.slice(startIdx, endIdx);

console.log(`Processing batch ${batchIndex + 1}/${totalBatches} (${batch.length} records) - ${elapsedTime/1000}s elapsed`);
console.log(`Current results so far: ${result.imported} imported, ${result.skipped} skipped, ${result.errors} errors`);


try {
// Process batch with transaction for better performance
await processBatch(batch, userId, stationId, result);

// Log progress every 10 batches
if ((batchIndex + 1) % 10 === 0) {
console.log(`Progress: ${batchIndex + 1}/${totalBatches} batches completed. ${result.imported} imported, ${result.skipped} skipped, ${result.errors} errors.`);
}
} catch (batchError) {
console.error(`Batch ${batchIndex + 1} failed:`, batchError);
result.errors += batch.length; // Mark all records in batch as errors
Expand Down Expand Up @@ -209,30 +199,21 @@ async function parseAndImportADIF(content: string, userId: number, stationId: nu
}

async function processBatch(records: ADIFRecord[], userId: number, stationId: number, result: ImportResult): Promise<void> {
console.log(`Starting batch of ${records.length} records...`);

for (let i = 0; i < records.length; i++) {
const record = records[i];
try {
await importRecord(record, userId, stationId, result);

// Log progress every 10 records within batch
if ((i + 1) % 10 === 0) {
console.log(` Processed ${i + 1}/${records.length} records in current batch`);
}
} catch (error) {
result.errors++;
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
console.error(`Record ${i + 1} import error:`, errorMsg);

// Only store first 10 error details to avoid memory issues
if (result.details && result.details.length < 10) {
result.details.push(`Error importing record ${i + 1}: ${errorMsg}`);
}
}
}

console.log(`Batch completed: ${result.imported} total imported, ${result.skipped} total skipped, ${result.errors} total errors`);
}

function parseADIFRecords(content: string): ADIFRecord[] {
Expand Down
18 changes: 2 additions & 16 deletions src/app/api/contacts/qrz-download/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,12 @@ export async function POST(request: NextRequest) {
}, { status: 400 });
}

console.log(`Starting QRZ download for ${stationsWithKeys.length} stations`);

for (const station of stationsWithKeys) {
try {
console.log(`Processing station: ${station.callsign} (${station.station_name})`);

// Download QSOs from QRZ for this station
const downloadResult = await downloadQSOsFromQRZ(station.qrz_api_key!, since);

if (!downloadResult.success) {
console.log(`QRZ download failed for station ${station.callsign}: ${downloadResult.error}`);
results.push({
stationId: station.id,
stationCallsign: station.callsign,
Expand All @@ -59,14 +54,10 @@ export async function POST(request: NextRequest) {
continue;
}

console.log(`Downloaded ${downloadResult.qsos.length} QSOs from QRZ for station ${station.callsign}`);

// Get contacts for this station that were sent to QRZ but not confirmed
const unconfirmedContacts = await Contact.findQrzSentNotConfirmed(decoded.userId);
const stationContacts = unconfirmedContacts.filter(c => c.station_id === station.id);

console.log(`Found ${stationContacts.length} unconfirmed contacts for station ${station.callsign}`);


let confirmationsFound = 0;

// Annotate each contact with the station callsign so the matcher can
Expand All @@ -93,7 +84,6 @@ export async function POST(request: NextRequest) {
break;
}

console.log(`Marking ${contact.callsign} as QRZ confirmed (status=${qrzQSO.app_qrzlog_status ?? '<missing>'})`);
await Contact.updateQrzQsl(contact.id, undefined, 'Y');
confirmationsFound++;

Expand Down Expand Up @@ -122,8 +112,6 @@ export async function POST(request: NextRequest) {

} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
console.log(`Exception processing station ${station.callsign}: ${errorMessage}`);

results.push({
stationId: station.id,
stationCallsign: station.callsign,
Expand All @@ -133,8 +121,6 @@ export async function POST(request: NextRequest) {
}
}

console.log(`QRZ download completed. Processed ${stationsProcessed.size} stations`);

// Calculate summary
const successful = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
Expand Down
60 changes: 6 additions & 54 deletions src/app/api/contacts/qrz-sync/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,12 @@ export async function POST(request: NextRequest) {

const results = [];

console.log(`Starting QRZ upload for ${contactIds.length} contacts`);

// Process each contact
for (const contactId of contactIds) {
try {
console.log(`Processing contact ID: ${contactId}`);

// Get the contact
const contact = await Contact.findById(contactId);
if (!contact || contact.user_id !== decoded.userId) {
console.log(`Contact ${contactId}: Not found or access denied`);
results.push({
contactId,
success: false,
Expand All @@ -48,12 +43,8 @@ export async function POST(request: NextRequest) {
continue;
}

console.log(`Contact ${contactId}: Found contact for ${contact.callsign} on ${contact.datetime}`);


// Skip if already sent to QRZ
if (contact.qrz_qsl_sent === 'Y') {
console.log(`Contact ${contactId}: Already sent to QRZ, skipping`);
results.push({
contactId,
success: true,
Expand All @@ -65,7 +56,6 @@ export async function POST(request: NextRequest) {

// If we've received confirmation from QRZ, mark as sent too (it exists in QRZ)
if (contact.qrz_qsl_rcvd === 'Y') {
console.log(`Contact ${contactId}: Already confirmed by QRZ - marking as sent in our database`);
await Contact.updateQrzQsl(contactId, 'Y');
results.push({
contactId,
Expand All @@ -78,7 +68,6 @@ export async function POST(request: NextRequest) {

// Get the station for this contact to get QRZ API key
if (!contact.station_id) {
console.log(`Contact ${contactId}: No station_id associated`);
results.push({
contactId,
success: false,
Expand All @@ -87,10 +76,8 @@ export async function POST(request: NextRequest) {
continue;
}

console.log(`Contact ${contactId}: Looking up station ID ${contact.station_id}`);
const station = await Station.findByUserIdAndId(decoded.userId, contact.station_id);
if (!station) {
console.log(`Contact ${contactId}: Station ${contact.station_id} not found`);
results.push({
contactId,
success: false,
Expand All @@ -99,10 +86,7 @@ export async function POST(request: NextRequest) {
continue;
}

console.log(`Contact ${contactId}: Found station ${station.callsign} (${station.station_name})`);

if (!station.qrz_api_key) {
console.log(`Contact ${contactId}: Station ${station.callsign} has no QRZ API key`);
results.push({
contactId,
success: false,
Expand All @@ -111,55 +95,32 @@ export async function POST(request: NextRequest) {
continue;
}

console.log(`Contact ${contactId}: Station has QRZ API key, proceeding with sync`);


// Convert contact to QRZ format
console.log(`Contact ${contactId}: Converting to QRZ format`);
// Convert contact to QRZ format and upload
const qrzData = contactToQRZFormat(contact);
console.log(`Contact ${contactId}: QRZ data:`, {
call: qrzData.call,
qso_date: qrzData.qso_date,
time_on: qrzData.time_on,
band: qrzData.band,
mode: qrzData.mode
});

// Upload to QRZ using API key
console.log(`Contact ${contactId}: Uploading to QRZ with API key`);
const uploadResult = await uploadQSOToQRZWithApiKey(qrzData, station.qrz_api_key);
console.log(`Contact ${contactId}: QRZ upload result:`, uploadResult);

if (uploadResult.success) {
if (uploadResult.already_exists) {
console.log(`Contact ${contactId}: QSO already exists in QRZ - marking as both sent and received in our database`);
// Mark as both sent AND received since it exists in QRZ (it's confirmed!)
const updateResult = await Contact.updateQrzQsl(contactId, 'Y', 'Y');
console.log(`Contact ${contactId}: Database update result:`, updateResult ? 'success' : 'failed');

await Contact.updateQrzQsl(contactId, 'Y', 'Y');
results.push({
contactId,
success: true,
already_existed: true,
message: 'QSO already exists in QRZ logbook (marked as sent and confirmed)'
});
} else {
console.log(`Contact ${contactId}: QRZ upload successful - marking as sent in our database`);
// Mark as sent to QRZ (but not yet confirmed)
const updateResult = await Contact.updateQrzQsl(contactId, 'Y');
console.log(`Contact ${contactId}: Database update result:`, updateResult ? 'success' : 'failed');

await Contact.updateQrzQsl(contactId, 'Y');
results.push({
contactId,
success: true,
message: 'Successfully sent to QRZ'
});
}
} else {
console.log(`Contact ${contactId}: QRZ upload failed: ${uploadResult.error}`);
// Mark as request failed
await Contact.updateQrzQsl(contactId, 'R');

results.push({
contactId,
success: false,
Expand All @@ -170,9 +131,7 @@ export async function POST(request: NextRequest) {
} catch (error) {
// Mark as error
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
console.log(`Contact ${contactId}: Exception occurred: ${errorMessage}`);
await Contact.updateQrzQsl(contactId, 'R');

results.push({
contactId,
success: false,
Expand All @@ -181,13 +140,6 @@ export async function POST(request: NextRequest) {
}
}

console.log(`QRZ upload completed. Processing summary:`);
console.log(`- Total contacts: ${contactIds.length}`);
console.log(`- Successfully sent: ${results.filter(r => r.success).length}`);
console.log(`- Failed: ${results.filter(r => !r.success).length}`);
console.log(`- Skipped: ${results.filter(r => r.skipped).length}`);
console.log(`- Already existed: ${results.filter(r => r.already_existed).length}`);

// Calculate summary
const successful = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
Expand All @@ -207,8 +159,8 @@ export async function POST(request: NextRequest) {

} catch (error) {
console.error('QRZ sync error:', error);
return NextResponse.json({
error: 'Failed to sync with QRZ'
return NextResponse.json({
error: 'Failed to sync with QRZ'
}, { status: 500 });
}
}
}
18 changes: 0 additions & 18 deletions src/app/api/cron/lotw-download/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,6 @@ import { query } from '@/lib/db';

export async function GET(request: NextRequest) {
try {
// Enhanced error logging for troubleshooting
console.log('LoTW download cron job authentication check...');
console.log('Request headers (excluding sensitive data):', {
'user-agent': request.headers.get('user-agent'),
'x-vercel-id': request.headers.get('x-vercel-id'),
'x-forwarded-for': request.headers.get('x-forwarded-for'),
'host': request.headers.get('host'),
'has-authorization': !!request.headers.get('authorization'),
'cron-secret-configured': !!process.env.CRON_SECRET
});

// Environment validation
const requiredEnvVars = ['DATABASE_URL', 'JWT_SECRET', 'ENCRYPTION_SECRET'];
const missingEnvVars = requiredEnvVars.filter(varName => !process.env[varName]);
Expand Down Expand Up @@ -48,8 +37,6 @@ export async function GET(request: NextRequest) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

console.log('Starting LoTW download cron job...');

// Get all active stations that have LoTW credentials configured
const stationsResult = await query(`
SELECT DISTINCT s.id, s.callsign, s.user_id
Expand Down Expand Up @@ -77,7 +64,6 @@ export async function GET(request: NextRequest) {
);

if (recentDownloadResult.rows.length > 0) {
console.log(`Skipping station ${station.callsign} - downloaded recently`);
results.push({
station_id: station.id,
callsign: station.callsign,
Expand Down Expand Up @@ -121,8 +107,6 @@ export async function GET(request: NextRequest) {
error: downloadResponse.ok ? null : downloadData.error
});

console.log(`Station ${station.callsign}: ${downloadResponse.ok ? 'success' : 'error'} - ${downloadData.confirmations_found || 0} confirmations found, ${downloadData.confirmations_matched || 0} matched`);

} catch (stationError) {
console.error(`Error processing station ${station.callsign}:`, stationError);
results.push({
Expand All @@ -134,8 +118,6 @@ export async function GET(request: NextRequest) {
}
}

console.log('LoTW download cron job completed');

return NextResponse.json({
success: true,
processed_stations: results.length,
Expand Down
Loading
Loading