diff --git a/server/database-storage.ts b/server/database-storage.ts index aa7e8aa..1c204e3 100644 --- a/server/database-storage.ts +++ b/server/database-storage.ts @@ -602,89 +602,108 @@ export class DatabaseStorage implements IStorage { async updateDnsRecord(id: string, recordData: Partial): Promise { try { + console.log(`Updating DNS record with ID: ${id}, data:`, JSON.stringify(recordData)); + + // Use direct PostgreSQL for consistency with other methods + const { pool } = await import('./db'); + // Filter out fields that don't exist in the database const { proxied, isAutoIP, providerId, ...validFields } = recordData as any; - // If there's only isAutoIP or no fields at all, handle it separately + // Simple case: Just update the timestamp if (Object.keys(validFields).length === 0 && isAutoIP === undefined) { - // Just update the last_updated timestamp - const updateQuery = sql` + console.log(`Simple update for record ${id} - just updating timestamp`); + + const simpleQueryText = ` UPDATE dns_records - SET last_updated = ${new Date()} - WHERE id = ${id} + SET last_updated = NOW() + WHERE id = $1 RETURNING id, domain_id, name, type, content, ttl, priority, provider_record_id, is_active, auto_update, notes, last_updated, created_at `; - const updateResult = await db.execute(updateQuery); + const simpleResult = await pool.query(simpleQueryText, [id]); - if (!Array.isArray(updateResult) || updateResult.length === 0) { + if (!simpleResult.rows || simpleResult.rows.length === 0) { + console.log(`No record found with ID ${id} for simple update`); return undefined; } - const record = updateResult[0]; + const simpleRecord = simpleResult.rows[0]; + console.log(`Updated record with simple query:`, simpleRecord); + return { - id: record.id, - domainId: record.domain_id, - name: record.name, - type: record.type, - content: record.content, - ttl: record.ttl, - priority: record.priority, - providerRecordId: record.provider_record_id, - isActive: record.is_active, - isAutoIP: record.auto_update, - notes: record.notes, - lastUpdated: record.last_updated, - createdAt: record.created_at, + id: simpleRecord.id, + domainId: simpleRecord.domain_id, + name: simpleRecord.name, + type: simpleRecord.type, + content: simpleRecord.content, + ttl: simpleRecord.ttl, + priority: simpleRecord.priority, + providerRecordId: simpleRecord.provider_record_id, + isActive: simpleRecord.is_active, + isAutoIP: simpleRecord.auto_update, + notes: simpleRecord.notes, + lastUpdated: simpleRecord.last_updated, + createdAt: simpleRecord.created_at, proxied: null // Maintained for frontend compatibility }; } - // Create dynamic SQL for the update - // Start with base query - let sqlQuery = sql`UPDATE dns_records SET `; + // Complex case: Build a dynamic query with proper parameters + console.log(`Complex update for record ${id} with fields:`, Object.keys(validFields)); - // Add each field dynamically - const setFragments = []; + // Prepare the SET clause and parameters array + let setClause = ''; + const params = [id]; // First parameter is always the ID + let paramIndex = 2; // Start parameter indexing at 2 - // Process all valid fields + // Process valid fields and convert camelCase to snake_case Object.entries(validFields).forEach(([key, value]) => { // Convert camelCase to snake_case for database const dbField = key.replace(/([A-Z])/g, '_$1').toLowerCase(); - setFragments.push(sql`${sql.raw(dbField)} = ${value}`); + + // Add to SET clause + if (setClause) setClause += ', '; + setClause += `${dbField} = $${paramIndex}`; + params.push(value); + paramIndex++; }); - // Add isAutoIP if it exists (maps to auto_update in database) + // Add isAutoIP if present (maps to auto_update in database) if (isAutoIP !== undefined) { - setFragments.push(sql`auto_update = ${isAutoIP}`); + if (setClause) setClause += ', '; + setClause += `auto_update = $${paramIndex}`; + params.push(isAutoIP); + paramIndex++; } - // Always update last_updated - setFragments.push(sql`last_updated = ${new Date()}`); + // Always update last_updated timestamp + if (setClause) setClause += ', '; + setClause += `last_updated = $${paramIndex}`; + params.push(new Date()); - // Join all the SET fragments - for (let i = 0; i < setFragments.length; i++) { - sqlQuery = sql`${sqlQuery}${setFragments[i]}`; - if (i < setFragments.length - 1) { - sqlQuery = sql`${sqlQuery}, `; - } - } - - // Add WHERE and RETURNING - sqlQuery = sql`${sqlQuery} WHERE id = ${id} + // Complete query with RETURNING clause + const complexQueryText = ` + UPDATE dns_records + SET ${setClause} + WHERE id = $1 RETURNING id, domain_id, name, type, content, ttl, priority, - provider_record_id, is_active, auto_update, notes, last_updated, created_at`; + provider_record_id, is_active, auto_update, notes, last_updated, created_at + `; - // Execute the query - const result = await db.execute(sqlQuery); + console.log(`Executing update query with params:`, params); + const result = await pool.query(complexQueryText, params); - if (!Array.isArray(result) || result.length === 0) { + if (!result.rows || result.rows.length === 0) { + console.log(`No record found with ID ${id} for complex update`); return undefined; } + const updatedRecord = result.rows[0]; + console.log(`Updated record with complex query:`, updatedRecord); + // Map result to expected format - const updatedRecord = result[0]; return { id: updatedRecord.id, domainId: updatedRecord.domain_id, diff --git a/server/routes.ts b/server/routes.ts index abb55af..208d090 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -182,6 +182,9 @@ export async function registerRoutes(app: Express): Promise { app.put("/api/dns-records/:id", requireRole(["admin", "manager"]), async (req, res) => { try { + console.log("Processing DNS record update for id:", req.params.id); + console.log("Update payload:", JSON.stringify(req.body)); + const recordId = req.params.id; const validatedData = insertDnsRecordSchema.partial().parse(req.body); @@ -193,42 +196,62 @@ export async function registerRoutes(app: Express): Promise { // Get current record for history const currentRecord = await storage.getDnsRecord(recordId); if (!currentRecord) { + console.log(`DNS record not found with ID: ${recordId}`); return res.status(404).json({ message: "DNS record not found" }); } + console.log(`Found existing record:`, JSON.stringify(currentRecord)); + // Get domain to retrieve providerId if needed if (!validatedData.providerId) { const domain = await storage.getDomain(currentRecord.domainId); if (domain) { + console.log(`Using domain provider ID from domain:`, domain.providerId); validatedData.providerId = domain.providerId; } } + // Log what we're about to update + console.log(`Updating record with data:`, JSON.stringify(validatedData)); + + // Update the record const record = await storage.updateDnsRecord(recordId, validatedData); if (!record) { - return res.status(404).json({ message: "DNS record not found" }); + console.log(`Update failed - record not found or update failed`); + return res.status(404).json({ message: "DNS record not found or update failed" }); } - // Track history - await storage.addDnsHistory( - recordId, - "update", - JSON.stringify(currentRecord), - JSON.stringify(record), - req.user?.id - ); + console.log(`Record updated successfully:`, JSON.stringify(record)); - // Trigger webhooks - await triggerDnsWebhooks("update", record.domainId, record, currentRecord, req.user?.id); + try { + // Track history + await storage.addDnsHistory( + recordId, + "update", + JSON.stringify(currentRecord), + JSON.stringify(record), + req.user?.id + ); + + // Trigger webhooks + await triggerDnsWebhooks("update", record.domainId, record, currentRecord, req.user?.id); + } catch (historyError) { + // Don't fail the request if history tracking fails + console.error("Error tracking history:", historyError); + } res.json(record); } catch (error) { if (error instanceof z.ZodError) { + console.error("Validation error:", error.errors); res.status(400).json({ message: "Validation error", errors: error.errors }); } else { console.error("Error updating DNS record:", error); - res.status(500).json({ message: "Internal server error" }); + res.status(500).json({ + message: "Failed to update DNS record", + error: error instanceof Error ? error.message : String(error) + }); } } }); @@ -236,35 +259,54 @@ export async function registerRoutes(app: Express): Promise { app.delete("/api/dns-records/:id", requireRole(["admin", "manager"]), async (req, res) => { try { const recordId = req.params.id; + console.log(`Processing DNS record deletion for ID: ${recordId}`); // Get current record for history and webhooks const currentRecord = await storage.getDnsRecord(recordId); if (!currentRecord) { + console.log(`DNS record not found for deletion with ID: ${recordId}`); return res.status(404).json({ message: "DNS record not found" }); } + console.log(`Found record to delete:`, JSON.stringify(currentRecord)); + + // Perform the deletion const success = await storage.deleteDnsRecord(recordId); if (!success) { - return res.status(404).json({ message: "DNS record not found" }); + console.log(`Delete operation failed for record ID: ${recordId}`); + return res.status(404).json({ message: "DNS record not found or could not be deleted" }); } - // Track history - await storage.addDnsHistory( - recordId, - "delete", - JSON.stringify(currentRecord), - undefined, - req.user?.id - ); + console.log(`Successfully deleted record with ID: ${recordId}`); - // Trigger webhooks - await triggerDnsWebhooks("delete", currentRecord.domainId, null, currentRecord, req.user?.id); + try { + // Track history + await storage.addDnsHistory( + recordId, + "delete", + JSON.stringify(currentRecord), + undefined, + req.user?.id + ); + + // Trigger webhooks + await triggerDnsWebhooks("delete", currentRecord.domainId, null, currentRecord, req.user?.id); + + console.log(`History and webhooks processed for deleted record ${recordId}`); + } catch (historyError) { + // Don't fail the request if history tracking fails + console.error("Error tracking history for deleted record:", historyError); + } + // Return success res.status(204).end(); } catch (error) { console.error("Error deleting DNS record:", error); - res.status(500).json({ message: "Internal server error" }); + res.status(500).json({ + message: "Failed to delete DNS record", + error: error instanceof Error ? error.message : String(error) + }); } });