import { 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 session from "express-session"; import { DatabaseStorage } from "./database-storage"; export interface IStorage { // User management getUser(id: string): Promise; getUserByUsername(username: string): Promise; getUserByEmail(email: string): Promise; createUser(user: InsertUser): Promise; updateUser(id: string, user: Partial): Promise; deleteUser(id: string): Promise; // Organization management getOrganization(id: string): Promise; getOrganizations(): Promise; createOrganization(org: InsertOrganization): Promise; updateOrganization(id: string, org: Partial): Promise; deleteOrganization(id: string): Promise; // Domain management getDomain(id: string): Promise; getDomainsByOrganization(organizationId: string): Promise; getAllDomains(): Promise; createDomain(domain: InsertDomain): Promise; updateDomain(id: string, domain: Partial): Promise; deleteDomain(id: string): Promise; // DNS Record management getDnsRecord(id: string): Promise; getDnsRecordsByDomain(domainId: string): Promise; createDnsRecord(record: InsertDnsRecord): Promise; updateDnsRecord(id: string, record: Partial): Promise; deleteDnsRecord(id: string): Promise; // Provider management getProvider(id: string): Promise; getProviders(): Promise; createProvider(provider: InsertProvider): Promise; updateProvider(id: string, provider: Partial): Promise; deleteProvider(id: string): Promise; // API Token management getApiToken(id: string): Promise; getApiTokenByToken(token: string): Promise; getApiTokensByOrganization(organizationId: string): Promise; createApiToken(token: InsertApiToken): Promise; updateApiToken(id: string, token: Partial): Promise; deleteApiToken(id: string): Promise; // DNS History addDnsHistory(recordId: string, action: string, previousValue?: string, newValue?: string, userId?: string): Promise; 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; // Webhook Delivery Logs addWebhookDeliveryLog(log: InsertWebhookDeliveryLog): Promise; getWebhookDeliveryLog(id: string): Promise; getWebhookDeliveryLogsByWebhook(webhookId: string): Promise; // Session store sessionStore: any; } export class MemStorage implements IStorage { private usersMap: Map; private orgsMap: Map; private domainsMap: Map; private recordsMap: Map; private providersMap: Map; private apiTokensMap: Map; private historyMap: Map; private webhooksMap: Map; private webhookDeliveryLogsMap: Map; // Counters for IDs private userIdCounter: number; private orgIdCounter: number; private domainIdCounter: number; private recordIdCounter: number; private providerIdCounter: number; private apiTokenIdCounter: number; private historyIdCounter: number; private webhookIdCounter: number; private webhookDeliveryLogIdCounter: number; public sessionStore: any; constructor() { this.usersMap = new Map(); this.orgsMap = new Map(); this.domainsMap = new Map(); this.recordsMap = new Map(); this.providersMap = new Map(); this.apiTokensMap = new Map(); this.historyMap = new Map(); this.webhooksMap = new Map(); this.webhookDeliveryLogsMap = new Map(); this.userIdCounter = 1; this.orgIdCounter = 1; this.domainIdCounter = 1; this.recordIdCounter = 1; this.providerIdCounter = 1; this.apiTokenIdCounter = 1; this.historyIdCounter = 1; this.webhookIdCounter = 1; this.webhookDeliveryLogIdCounter = 1; // Session store is created in the DatabaseStorage class this.sessionStore = null; // Initialize sample data this.initSampleData(); } private initSampleData() { // Create a default provider for Cloudflare const cloudflareProvider: InsertProvider = { name: "Cloudflare", type: "cloudflare", credentials: { apiKey: process.env.CLOUDFLARE_API_KEY || "" }, isActive: true }; this.createProvider(cloudflareProvider); // Create sample organization const defaultOrg: InsertOrganization = { name: "Default Organization", isActive: true }; this.createOrganization(defaultOrg); } // Users async getUser(id: string): Promise { return this.usersMap.get(parseInt(id)); } async getUserByUsername(username: string): Promise { return Array.from(this.usersMap.values()).find( (user) => user.username === username, ); } async getUserByEmail(email: string): Promise { return Array.from(this.usersMap.values()).find( (user) => user.email === email, ); } async createUser(user: InsertUser): Promise { const numId = this.userIdCounter++; const createdAt = new Date(); const newUser: User = { id: numId.toString(), username: user.username, password: user.password, email: user.email, fullName: user.fullName || null, role: user.role || 'user', organizationId: user.organizationId || null, createdAt }; this.usersMap.set(numId, newUser); return newUser; } async updateUser(id: string, userData: Partial): Promise { const numId = parseInt(id); const user = await this.getUser(id); if (!user) return undefined; const updatedUser = { ...user, ...userData }; this.usersMap.set(numId, updatedUser); return updatedUser; } async deleteUser(id: string): Promise { return this.usersMap.delete(parseInt(id)); } // Organizations async getOrganization(id: string): Promise { return this.orgsMap.get(parseInt(id)); } async getOrganizations(): Promise { return Array.from(this.orgsMap.values()); } async createOrganization(org: InsertOrganization): Promise { const numId = this.orgIdCounter++; const createdAt = new Date(); const newOrg: Organization = { id: numId.toString(), name: org.name, isActive: org.isActive ?? true, createdAt }; this.orgsMap.set(numId, newOrg); return newOrg; } async updateOrganization(id: string, orgData: Partial): Promise { const numId = parseInt(id); const org = await this.getOrganization(id); if (!org) return undefined; const updatedOrg = { ...org, ...orgData }; this.orgsMap.set(numId, updatedOrg); return updatedOrg; } async deleteOrganization(id: string): Promise { return this.orgsMap.delete(parseInt(id)); } // Domains async getDomain(id: string): Promise { return this.domainsMap.get(parseInt(id)); } async getDomainsByOrganization(organizationId: string): Promise { return Array.from(this.domainsMap.values()) .filter(domain => domain.organizationId === organizationId); } async getAllDomains(): Promise { return Array.from(this.domainsMap.values()); } async createDomain(domain: InsertDomain): Promise { const numId = this.domainIdCounter++; const createdAt = new Date(); const lastUpdated = new Date(); const newDomain: Domain = { id: numId.toString(), name: domain.name, organizationId: domain.organizationId, providerId: domain.providerId, isActive: domain.isActive ?? true, lastUpdated, createdAt }; this.domainsMap.set(numId, newDomain); return newDomain; } async updateDomain(id: string, domainData: Partial): Promise { const numId = parseInt(id); const domain = await this.getDomain(id); if (!domain) return undefined; const updatedDomain = { ...domain, ...domainData, lastUpdated: new Date() }; this.domainsMap.set(numId, updatedDomain); return updatedDomain; } async deleteDomain(id: string): Promise { return this.domainsMap.delete(parseInt(id)); } // DNS Records async getDnsRecord(id: string): Promise { return this.recordsMap.get(parseInt(id)); } async getDnsRecordsByDomain(domainId: string): Promise { return Array.from(this.recordsMap.values()) .filter(record => record.domainId === domainId); } async createDnsRecord(record: InsertDnsRecord): Promise { const numId = this.recordIdCounter++; const createdAt = new Date(); const lastUpdated = new Date(); const newRecord: DnsRecord = { id: numId.toString(), domainId: record.domainId, name: record.name, type: record.type, content: record.content, ttl: record.ttl ?? 3600, proxied: record.proxied ?? false, isActive: record.isActive ?? true, isAutoIP: record.isAutoIP ?? false, notes: record.notes ?? null, lastUpdated, createdAt }; this.recordsMap.set(numId, newRecord); return newRecord; } async updateDnsRecord(id: string, recordData: Partial): Promise { const numId = parseInt(id); const record = await this.getDnsRecord(id); if (!record) return undefined; const updatedRecord = { ...record, ...recordData, lastUpdated: new Date() }; this.recordsMap.set(numId, updatedRecord); return updatedRecord; } async deleteDnsRecord(id: string): Promise { return this.recordsMap.delete(parseInt(id)); } // Providers async getProvider(id: string): Promise { return this.providersMap.get(parseInt(id)); } async getProviders(): Promise { return Array.from(this.providersMap.values()); } async createProvider(provider: InsertProvider): Promise { const numId = this.providerIdCounter++; const createdAt = new Date(); const newProvider: Provider = { id: numId.toString(), name: provider.name, type: provider.type, credentials: provider.credentials ?? null, isActive: provider.isActive ?? true, createdAt }; this.providersMap.set(numId, newProvider); return newProvider; } async updateProvider(id: string, providerData: Partial): Promise { const numId = parseInt(id); const provider = await this.getProvider(id); if (!provider) return undefined; const updatedProvider = { ...provider, ...providerData }; this.providersMap.set(numId, updatedProvider); return updatedProvider; } async deleteProvider(id: string): Promise { return this.providersMap.delete(parseInt(id)); } // API Tokens async getApiToken(id: string): Promise { return this.apiTokensMap.get(parseInt(id)); } async getApiTokenByToken(token: string): Promise { return Array.from(this.apiTokensMap.values()) .find(apiToken => apiToken.token === token); } async getApiTokensByOrganization(organizationId: string): Promise { return Array.from(this.apiTokensMap.values()) .filter(token => token.organizationId === organizationId); } async createApiToken(token: InsertApiToken): Promise { const numId = this.apiTokenIdCounter++; const createdAt = new Date(); const newToken: ApiToken = { id: numId.toString(), name: token.name, token: token.token, organizationId: token.organizationId, permissions: token.permissions ?? null, createdBy: token.createdBy, isActive: token.isActive ?? true, expiresAt: token.expiresAt ?? null, createdAt }; this.apiTokensMap.set(numId, newToken); return newToken; } async updateApiToken(id: string, tokenData: Partial): Promise { const numId = parseInt(id); const token = await this.getApiToken(id); if (!token) return undefined; const updatedToken = { ...token, ...tokenData }; this.apiTokensMap.set(numId, updatedToken); return updatedToken; } async deleteApiToken(id: string): Promise { return this.apiTokensMap.delete(parseInt(id)); } // DNS History async addDnsHistory( recordId: string, action: string, previousValue?: string, newValue?: string, userId?: string ): Promise { const numId = this.historyIdCounter++; const timestamp = new Date(); const historyEntry: DnsHistory = { id: numId.toString(), recordId, action, previousValue: previousValue ?? null, newValue: newValue ?? null, userId: userId ?? null, timestamp }; this.historyMap.set(numId, historyEntry); return historyEntry; } async getDnsHistoryByRecord(recordId: string): Promise { return Array.from(this.historyMap.values()) .filter(history => history.recordId === recordId) .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); } async getDnsHistoryByDomain(domainId: string): Promise { // Get all records for domain const records = await this.getDnsRecordsByDomain(domainId); const recordIds = records.map(r => r.id); // Get history entries for all records return Array.from(this.historyMap.values()) .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 { // 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 const updatedWebhook = { ...webhook, lastTriggered: new Date() }; this.webhooksMap.set(parseInt(webhookId), updatedWebhook); return true; } catch (error) { console.error(`Error triggering webhook ${webhook.id}:`, error); // Log the failed delivery attempt await this.addWebhookDeliveryLog({ webhookId: webhook.id, event: payload.event || 'unknown', payload, signature: '', status: false, message: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, retryCount: 0 }); return false; } } // Webhook Delivery Logs async addWebhookDeliveryLog(log: InsertWebhookDeliveryLog): Promise { const numId = this.webhookDeliveryLogIdCounter++; const createdAt = new Date(); const newLog: WebhookDeliveryLog = { id: numId.toString(), webhookId: log.webhookId, event: log.event, payload: log.payload, signature: log.signature || null, status: log.status, statusCode: log.statusCode || null, message: log.message, responseBody: log.responseBody || null, retryCount: log.retryCount || 0, createdAt }; this.webhookDeliveryLogsMap.set(numId, newLog); return newLog; } async getWebhookDeliveryLog(id: string): Promise { return this.webhookDeliveryLogsMap.get(parseInt(id)); } async getWebhookDeliveryLogsByWebhook(webhookId: string): Promise { return Array.from(this.webhookDeliveryLogsMap.values()) .filter(log => log.webhookId === webhookId) .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); } } export const storage = new DatabaseStorage();