diff --git a/server/database-storage.ts b/server/database-storage.ts index fe2854e..c202532 100644 --- a/server/database-storage.ts +++ b/server/database-storage.ts @@ -1,13 +1,13 @@ import { users, organizations, domains, dnsRecords, - providers, dnsHistory, apiTokens, + providers, dnsHistory, apiTokens, webhooks, 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 DnsHistory, type Webhook, type InsertWebhook } from "@shared/schema"; import { db } from "./db"; import { eq, and, desc, or, inArray, sql } from "drizzle-orm"; @@ -260,4 +260,55 @@ export class DatabaseStorage implements IStorage { ) .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; + + // 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); + + // 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); + return false; + } + } } \ No newline at end of file diff --git a/server/storage.ts b/server/storage.ts index 950bf32..c9c264d 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -5,7 +5,8 @@ import { type DnsRecord, type InsertDnsRecord, type Provider, type InsertProvider, type ApiToken, type InsertApiToken, - type DnsHistory + type DnsHistory, + type Webhook, type InsertWebhook } from "@shared/schema"; import session from "express-session"; import { DatabaseStorage } from "./database-storage"; @@ -61,6 +62,14 @@ export interface IStorage { getDnsHistoryByRecord(recordId: string): Promise; getDnsHistoryByDomain(domainId: string): Promise; + // Webhook management + getWebhook(id: string): Promise; + getWebhooksByOrganization(organizationId: string): Promise; + createWebhook(webhook: InsertWebhook): Promise; + updateWebhook(id: string, webhook: Partial): Promise; + deleteWebhook(id: string): Promise; + triggerWebhook(webhookId: string, payload: any): Promise; + // Session store sessionStore: any; } @@ -73,6 +82,7 @@ export class MemStorage implements IStorage { private providersMap: Map; private apiTokensMap: Map; private historyMap: Map; + private webhooksMap: Map; // Counters for IDs private userIdCounter: number; @@ -82,6 +92,7 @@ export class MemStorage implements IStorage { private providerIdCounter: number; private apiTokenIdCounter: number; private historyIdCounter: number; + private webhookIdCounter: number; public sessionStore: any; @@ -93,6 +104,7 @@ export class MemStorage implements IStorage { this.providersMap = new Map(); this.apiTokensMap = new Map(); this.historyMap = new Map(); + this.webhooksMap = new Map(); this.userIdCounter = 1; this.orgIdCounter = 1; @@ -101,6 +113,7 @@ export class MemStorage implements IStorage { this.providerIdCounter = 1; this.apiTokenIdCounter = 1; this.historyIdCounter = 1; + this.webhookIdCounter = 1; // Session store is created in the DatabaseStorage class this.sessionStore = null; @@ -436,6 +449,71 @@ export class MemStorage implements IStorage { .filter(history => recordIds.includes(history.recordId)) .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); } + + // Webhook management + async getWebhook(id: string): Promise { + return this.webhooksMap.get(parseInt(id)); + } + + async getWebhooksByOrganization(organizationId: string): Promise { + return Array.from(this.webhooksMap.values()) + .filter(webhook => webhook.organizationId === organizationId); + } + + async createWebhook(webhook: InsertWebhook): Promise { + const numId = this.webhookIdCounter++; + const createdAt = new Date(); + const newWebhook: Webhook = { + id: numId.toString(), + name: webhook.name, + url: webhook.url, + organizationId: webhook.organizationId, + secret: webhook.secret ?? null, + events: webhook.events, + isActive: webhook.isActive ?? true, + lastTriggered: null, + createdBy: webhook.createdBy, + createdAt + }; + this.webhooksMap.set(numId, newWebhook); + return newWebhook; + } + + async updateWebhook(id: string, webhookData: Partial): Promise { + const numId = parseInt(id); + const webhook = await this.getWebhook(id); + if (!webhook) return undefined; + + const updatedWebhook = { ...webhook, ...webhookData }; + this.webhooksMap.set(numId, updatedWebhook); + return updatedWebhook; + } + + async deleteWebhook(id: string): Promise { + return this.webhooksMap.delete(parseInt(id)); + } + + async triggerWebhook(webhookId: string, payload: any): Promise { + const webhook = await this.getWebhook(webhookId); + if (!webhook || !webhook.isActive) return false; + + try { + // 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); + + // Update the lastTriggered timestamp + const updatedWebhook = { + ...webhook, + lastTriggered: new Date() + }; + this.webhooksMap.set(parseInt(webhookId), updatedWebhook); + + return true; + } catch (error) { + console.error(`Error triggering webhook ${webhook.id}:`, error); + return false; + } + } } export const storage = new DatabaseStorage();