import { users, organizations, domains, dnsRecords, providers, dnsHistory, apiTokens, webhooks, webhookDeliveryLogs, type User, type InsertUser, type Organization, type InsertOrganization, type Domain, type InsertDomain, type DnsRecord, type InsertDnsRecord, type Provider, type InsertProvider, type ApiToken, type InsertApiToken, type DnsHistory, type Webhook, type InsertWebhook, type WebhookDeliveryLog, type InsertWebhookDeliveryLog } from "@shared/schema"; import { db } from "./db"; import { eq, and, desc, or, inArray, sql } from "drizzle-orm"; import { IStorage } from "./storage"; import session from "express-session"; import connectPg from "connect-pg-simple"; import { pool } from "./db"; const PostgresSessionStore = connectPg(session); export class DatabaseStorage implements IStorage { public sessionStore: any; constructor() { this.sessionStore = new PostgresSessionStore({ pool, createTableIfMissing: true }); } // User management async getUser(id: string): Promise { const [user] = await db.select().from(users).where(eq(users.id, id)); return user; } async getUserByUsername(username: string): Promise { const [user] = await db.select().from(users).where(eq(users.username, username)); return user; } async getUserByEmail(email: string): Promise { const [user] = await db.select().from(users).where(eq(users.email, email)); return user; } async createUser(user: InsertUser): Promise { const [newUser] = await db.insert(users).values(user).returning(); return newUser; } async updateUser(id: string, userData: Partial): Promise { const [updatedUser] = await db.update(users) .set(userData) .where(eq(users.id, id)) .returning(); return updatedUser; } async deleteUser(id: string): Promise { const result = await db.delete(users).where(eq(users.id, id)).returning(); return result.length > 0; } // Organization management async getOrganization(id: string): Promise { const [org] = await db.select().from(organizations).where(eq(organizations.id, id)); return org; } async getOrganizations(): Promise { return await db.select().from(organizations); } async createOrganization(org: InsertOrganization): Promise { const [newOrg] = await db.insert(organizations).values(org).returning(); return newOrg; } async updateOrganization(id: string, orgData: Partial): Promise { const [updatedOrg] = await db.update(organizations) .set(orgData) .where(eq(organizations.id, id)) .returning(); return updatedOrg; } async deleteOrganization(id: string): Promise { const result = await db.delete(organizations).where(eq(organizations.id, id)).returning(); return result.length > 0; } // Domain management async getDomain(id: string): Promise { const [domain] = await db.select().from(domains).where(eq(domains.id, id)); return domain; } async getDomainsByOrganization(organizationId: string): Promise { return await db.select() .from(domains) .where(eq(domains.organizationId, organizationId)); } async getAllDomains(): Promise { return await db.select().from(domains); } async createDomain(domain: InsertDomain): Promise { const [newDomain] = await db.insert(domains).values(domain).returning(); return newDomain; } async updateDomain(id: string, domainData: Partial): Promise { const [updatedDomain] = await db.update(domains) .set({ ...domainData, lastUpdated: new Date() }) .where(eq(domains.id, id)) .returning(); return updatedDomain; } async deleteDomain(id: string): Promise { const result = await db.delete(domains).where(eq(domains.id, id)).returning(); return result.length > 0; } // DNS Record management async getDnsRecord(id: string): Promise { const [record] = await db.select().from(dnsRecords).where(eq(dnsRecords.id, id)); return record; } async getDnsRecordsByDomain(domainId: string): Promise { return await db.select() .from(dnsRecords) .where(eq(dnsRecords.domainId, domainId)); } async createDnsRecord(record: InsertDnsRecord): Promise { const [newRecord] = await db.insert(dnsRecords).values(record).returning(); return newRecord; } async updateDnsRecord(id: string, recordData: Partial): Promise { const [updatedRecord] = await db.update(dnsRecords) .set({ ...recordData, lastUpdated: new Date() }) .where(eq(dnsRecords.id, id)) .returning(); return updatedRecord; } async deleteDnsRecord(id: string): Promise { const result = await db.delete(dnsRecords).where(eq(dnsRecords.id, id)).returning(); return result.length > 0; } // Provider management async getProvider(id: string): Promise { const [provider] = await db.select().from(providers).where(eq(providers.id, id)); return provider; } async getProviders(): Promise { return await db.select().from(providers); } async createProvider(provider: InsertProvider): Promise { const [newProvider] = await db.insert(providers).values(provider).returning(); return newProvider; } async updateProvider(id: string, providerData: Partial): Promise { const [updatedProvider] = await db.update(providers) .set(providerData) .where(eq(providers.id, id)) .returning(); return updatedProvider; } async deleteProvider(id: string): Promise { const result = await db.delete(providers).where(eq(providers.id, id)).returning(); return result.length > 0; } // API Token management async getApiToken(id: string): Promise { const [token] = await db.select().from(apiTokens).where(eq(apiTokens.id, id)); return token; } async getApiTokenByToken(token: string): Promise { const [apiToken] = await db.select().from(apiTokens).where(eq(apiTokens.token, token)); return apiToken; } async getApiTokensByOrganization(organizationId: string): Promise { return await db.select() .from(apiTokens) .where(eq(apiTokens.organizationId, organizationId)); } async createApiToken(token: InsertApiToken): Promise { const [newToken] = await db.insert(apiTokens).values(token).returning(); return newToken; } async updateApiToken(id: string, tokenData: Partial): Promise { const [updatedToken] = await db.update(apiTokens) .set(tokenData) .where(eq(apiTokens.id, id)) .returning(); return updatedToken; } async deleteApiToken(id: string): Promise { const result = await db.delete(apiTokens).where(eq(apiTokens.id, id)).returning(); return result.length > 0; } // DNS History async addDnsHistory( recordId: string, action: string, previousValue?: string, newValue?: string, userId?: string ): Promise { const [historyEntry] = await db.insert(dnsHistory).values({ recordId, action, previousValue, newValue, userId, }).returning(); return historyEntry; } async getDnsHistoryByRecord(recordId: string): Promise { return await db.select() .from(dnsHistory) .where(eq(dnsHistory.recordId, recordId)) .orderBy(desc(dnsHistory.timestamp)); } async getDnsHistoryByDomain(domainId: string): Promise { // We need to get all records for the domain first const records = await this.getDnsRecordsByDomain(domainId); const recordIds = records.map(r => r.id); if (recordIds.length === 0) { return []; } // Use SQL for the IN condition return await db.select() .from(dnsHistory) .where( sql`${dnsHistory.recordId} IN (${sql.join(recordIds, sql`, `)})` ) .orderBy(desc(dnsHistory.timestamp)); } // Webhook management async getWebhook(id: string): Promise { const [webhook] = await db.select().from(webhooks).where(eq(webhooks.id, id)); return webhook; } async getWebhooksByOrganization(organizationId: string): Promise { return await db.select() .from(webhooks) .where(eq(webhooks.organizationId, organizationId)); } async createWebhook(webhook: InsertWebhook): Promise { const [newWebhook] = await db.insert(webhooks).values(webhook).returning(); return newWebhook; } async updateWebhook(id: string, webhookData: Partial): Promise { const [updatedWebhook] = await db.update(webhooks) .set(webhookData) .where(eq(webhooks.id, id)) .returning(); return updatedWebhook; } async deleteWebhook(id: string): Promise { const result = await db.delete(webhooks).where(eq(webhooks.id, id)).returning(); return result.length > 0; } async triggerWebhook(webhookId: string, payload: any): Promise { try { // Get the webhook const webhook = await this.getWebhook(webhookId); if (!webhook || !webhook.isActive) return false; // Using the webhook utility to deliver the webhook const { generateSignature, deliverWebhook } = await import('./utils/webhook'); // In a real implementation, this would make an HTTP request to the webhook URL console.log(`Triggering webhook ${webhook.name} (${webhook.id}) with payload:`, payload); // Generate signature for the payload if a secret is set const signature = webhook.secret ? generateSignature(payload, webhook.secret) : ''; // In a real implementation, this would make the actual HTTP request // For now, simulate a successful delivery const deliveryResult = { success: true, statusCode: 200, message: 'Webhook delivered successfully (simulated)', timestamp: new Date(), responseBody: JSON.stringify({ success: true }), retryCount: 0 }; // Log the delivery attempt await this.addWebhookDeliveryLog({ webhookId: webhook.id, event: payload.event || 'unknown', payload, signature, status: deliveryResult.success, statusCode: deliveryResult.statusCode, message: deliveryResult.message, responseBody: deliveryResult.responseBody, retryCount: deliveryResult.retryCount }); // Update the lastTriggered timestamp await db.update(webhooks) .set({ lastTriggered: new Date() }) .where(eq(webhooks.id, webhookId)); return true; } catch (error) { console.error(`Error triggering webhook ${webhookId}:`, error); // Log the failed delivery attempt if (error instanceof Error) { await this.addWebhookDeliveryLog({ webhookId, event: payload?.event || 'unknown', payload, signature: '', status: false, message: `Error: ${error.message}`, retryCount: 0 }); } return false; } } // Webhook Delivery Logs async addWebhookDeliveryLog(log: InsertWebhookDeliveryLog): Promise { const [newLog] = await db.insert(webhookDeliveryLogs).values(log).returning(); return newLog; } async getWebhookDeliveryLog(id: string): Promise { const [log] = await db.select().from(webhookDeliveryLogs).where(eq(webhookDeliveryLogs.id, id)); return log; } async getWebhookDeliveryLogsByWebhook(webhookId: string): Promise { return await db.select() .from(webhookDeliveryLogs) .where(eq(webhookDeliveryLogs.webhookId, webhookId)) .orderBy(desc(webhookDeliveryLogs.createdAt)); } }