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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ backend/dist
backend/public/uploads/*
!backend/public/uploads
backend/backend/*
backend/private/*

#----------
# Frontend
Expand Down
5 changes: 4 additions & 1 deletion backend/src/datasources/PostgresDSMigrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import { DRADowngradeRequest } from '../models/DRADowngradeRequest.js';
import { DRAPromoCode } from '../models/DRAPromoCode.js';
import { DRAPromoCodeRedemption } from '../models/DRAPromoCodeRedemption.js';
import { DRAPaymentTransaction } from '../models/DRAPaymentTransaction.js';
import { DRALeadGenerator } from '../models/DRALeadGenerator.js';
import { DRALeadGeneratorLead } from '../models/DRALeadGeneratorLead.js';
import dotenv from 'dotenv';
dotenv.config();

Expand All @@ -69,7 +71,8 @@ export default new DataSource({
database: database,
synchronize: false,
logging: true,
entities: [DRAUsersPlatform, DRAProject, DRAProjectMember, DRAProjectInvitation, DRAVerificationCode, DRADataSource, DRADataModel, DRADataModelLineage, DRADataModelSource, DRATableMetadata, DRACrossSourceJoinCatalog, DRAEnterpriseQuery, DRADashboard, DRAArticle, DRAArticleCategory, DRAArticleVersion, DRACategory, DRASitemapEntry, DRADashboardExportMetaData, DRAAIDataModelConversation, DRAAIDataModelMessage, DRAAIInsightReport, DRAAIInsightMessage, DRADataModelRefreshHistory, DRAScheduledBackupRun, DRASubscriptionTier, DRAPlatformSettings, DRAAccountCancellation, DRAEmailPreferences, DRANotification, DRAMongoDBSyncHistory, SyncHistory, DRACampaign, DRACampaignChannel, DRACampaignOfflineData, DRAAIJoinSuggestion, DRAReport, DRAReportItem, DRAReportShareKey, DRAOrganization, DRAWorkspace, DRAOrganizationMember, DRAOrganizationInvitation, DRAWorkspaceMember, DRAOrganizationSubscription, DRAPaddleWebhookEvent, DRAEnterpriseContactRequest, DRADowngradeRequest, DRAPromoCode, DRAPromoCodeRedemption, DRAPaymentTransaction],
entities: [DRAUsersPlatform, DRAProject, DRAProjectMember, DRAProjectInvitation, DRAVerificationCode, DRADataSource, DRADataModel, DRADataModelLineage, DRADataModelSource, DRATableMetadata, DRACrossSourceJoinCatalog, DRAEnterpriseQuery, DRADashboard, DRAArticle, DRAArticleCategory, DRAArticleVersion, DRACategory, DRASitemapEntry, DRADashboardExportMetaData, DRAAIDataModelConversation, DRAAIDataModelMessage, DRAAIInsightReport, DRAAIInsightMessage, DRADataModelRefreshHistory, DRAScheduledBackupRun, DRASubscriptionTier, DRAPlatformSettings, DRAAccountCancellation, DRAEmailPreferences, DRANotification, DRAMongoDBSyncHistory, SyncHistory, DRACampaign, DRACampaignChannel, DRACampaignOfflineData, DRAAIJoinSuggestion, DRAReport, DRAReportItem, DRAReportShareKey, DRAOrganization, DRAWorkspace, DRAOrganizationMember, DRAOrganizationInvitation, DRAWorkspaceMember, DRAOrganizationSubscription, DRAPaddleWebhookEvent, DRAEnterpriseContactRequest, DRADowngradeRequest, DRAPromoCode, DRAPromoCodeRedemption, DRAPaymentTransaction,
DRALeadGenerator, DRALeadGeneratorLead],
subscribers: [],
migrations: ['./src/migrations/*.ts'],
});
7 changes: 4 additions & 3 deletions backend/src/datasources/PostgresDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ import { DRAEnterpriseContactRequest } from "../models/DRAEnterpriseContactReque
import { DRADowngradeRequest } from "../models/DRADowngradeRequest.js";
import { DRAPromoCode } from "../models/DRAPromoCode.js";
import { DRAPromoCodeRedemption } from "../models/DRAPromoCodeRedemption.js";
import { DRAPaymentTransaction } from "../models/DRAPaymentTransaction.js";
import dotenv from 'dotenv';
import { DRAPaymentTransaction } from "../models/DRAPaymentTransaction.js";import { DRALeadGenerator } from '../models/DRALeadGenerator.js';
import { DRALeadGeneratorLead } from '../models/DRALeadGeneratorLead.js';import dotenv from 'dotenv';
Comment on lines +52 to +53
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new lead generator entity imports were appended onto existing import lines, resulting in multiple imports on a single line. This is easy to miss in reviews and can create noisy diffs/merge conflicts. Please split these into separate properly formatted import statements.

Suggested change
import { DRAPaymentTransaction } from "../models/DRAPaymentTransaction.js";import { DRALeadGenerator } from '../models/DRALeadGenerator.js';
import { DRALeadGeneratorLead } from '../models/DRALeadGeneratorLead.js';import dotenv from 'dotenv';
import { DRAPaymentTransaction } from "../models/DRAPaymentTransaction.js";
import { DRALeadGenerator } from "../models/DRALeadGenerator.js";
import { DRALeadGeneratorLead } from "../models/DRALeadGeneratorLead.js";
import dotenv from "dotenv";

Copilot uses AI. Check for mistakes.
dotenv.config();

export class PostgresDataSource {
Expand All @@ -73,7 +73,8 @@ export class PostgresDataSource {
database: database,
synchronize: false,
logging: true,
entities: [DRAUsersPlatform, DRAProject, DRAVerificationCode, DRADataSource, DRADataModel, DRADataModelLineage, DRADataModelSource, DRATableMetadata, DRACrossSourceJoinCatalog, DRAEnterpriseQuery, DRADashboard, DRAArticle, DRAArticleCategory, DRAArticleVersion, DRACategory, DRASitemapEntry, DRADashboardExportMetaData, DRAAIDataModelConversation, DRAAIDataModelMessage, DRAAIInsightReport, DRAAIInsightMessage, DRADataModelRefreshHistory, DRAScheduledBackupRun, DRASubscriptionTier, DRAProjectMember, DRAProjectInvitation, DRANotification, DRAPlatformSettings, DRAAccountCancellation, DRAEmailPreferences, DRAMongoDBSyncHistory, SyncHistory, DRACampaign, DRACampaignChannel, DRAAIJoinSuggestion, DRAReport, DRAReportItem, DRAReportShareKey, DRAOrganization, DRAWorkspace, DRAOrganizationMember, DRAOrganizationInvitation, DRAWorkspaceMember, DRAOrganizationSubscription, DRACampaignOfflineData, DRAPaddleWebhookEvent, DRAEnterpriseContactRequest, DRADowngradeRequest, DRAPromoCode, DRAPromoCodeRedemption, DRAPaymentTransaction],
entities: [DRAUsersPlatform, DRAProject, DRAVerificationCode, DRADataSource, DRADataModel, DRADataModelLineage, DRADataModelSource, DRATableMetadata, DRACrossSourceJoinCatalog, DRAEnterpriseQuery, DRADashboard, DRAArticle, DRAArticleCategory, DRAArticleVersion, DRACategory, DRASitemapEntry, DRADashboardExportMetaData, DRAAIDataModelConversation, DRAAIDataModelMessage, DRAAIInsightReport, DRAAIInsightMessage, DRADataModelRefreshHistory, DRAScheduledBackupRun, DRASubscriptionTier, DRAProjectMember, DRAProjectInvitation, DRANotification, DRAPlatformSettings, DRAAccountCancellation, DRAEmailPreferences, DRAMongoDBSyncHistory, SyncHistory, DRACampaign, DRACampaignChannel, DRAAIJoinSuggestion, DRAReport, DRAReportItem, DRAReportShareKey, DRAOrganization, DRAWorkspace, DRAOrganizationMember, DRAOrganizationInvitation, DRAWorkspaceMember, DRAOrganizationSubscription, DRACampaignOfflineData, DRAPaddleWebhookEvent, DRAEnterpriseContactRequest, DRADowngradeRequest, DRAPromoCode, DRAPromoCodeRedemption, DRAPaymentTransaction,
DRALeadGenerator, DRALeadGeneratorLead],
subscribers: [],
// Only load TypeORM migration files (exclude utility scripts like migrate-articles-markdown.ts)
migrations: ['./src/migrations/*.ts'],
Expand Down
8 changes: 8 additions & 0 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ import offlineTracking from './routes/offlineTracking.js';
import reports from './routes/reports.js';
import marketing from './routes/marketing.js';
import paddle_webhook from './routes/paddle-webhook.js';
import lead_generators from './routes/lead-generators.js';
import admin_lead_generators from './routes/admin/lead-generators.js';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
Expand Down Expand Up @@ -322,6 +324,12 @@ app.use('/campaigns', offlineTracking);
app.use('/reports', reports);
app.use('/marketing', marketing);
app.use('/paddle', paddle_webhook);
app.use('/lead-generators', lead_generators);
app.use('/admin/lead-generators', admin_lead_generators);

// Ensure private upload directories exist
import fs from 'fs';
fs.mkdirSync(path.join(__dirname, '../private/lead-generators'), { recursive: true });

app.use('/uploads', express.static(path.join(__dirname, '../public/uploads')));
app.use('/', express.static(path.join(__dirname, '../public')));
Expand Down
64 changes: 64 additions & 0 deletions backend/src/migrations/1775900000000-CreateLeadGeneratorTables.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class CreateLeadGeneratorTables1775900000000 implements MigrationInterface {
name = 'CreateLeadGeneratorTables1775900000000'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE IF NOT EXISTS "dra_lead_generators" (
"id" SERIAL NOT NULL,
"title" character varying(255) NOT NULL,
"slug" character varying(255) NOT NULL,
"description" text,
"file_name" character varying(500) NOT NULL,
"original_file_name" character varying(500) NOT NULL,
"is_gated" boolean NOT NULL DEFAULT true,
"is_active" boolean NOT NULL DEFAULT true,
"view_count" integer NOT NULL DEFAULT 0,
"download_count" integer NOT NULL DEFAULT 0,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT now(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT "PK_dra_lead_generators" PRIMARY KEY ("id")
)
`);

await queryRunner.query(`
CREATE UNIQUE INDEX IF NOT EXISTS "UQ_dra_lead_generators_slug"
ON "dra_lead_generators" ("slug")
`);

await queryRunner.query(`
CREATE TABLE IF NOT EXISTS "dra_lead_generator_leads" (
"id" SERIAL NOT NULL,
"lead_generator_id" integer NOT NULL,
"full_name" character varying(255),
"email" character varying(255) NOT NULL,
"company" character varying(255),
"phone" character varying(50),
"job_title" character varying(255),
"ip_address" character varying(45),
"created_at" TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT "PK_dra_lead_generator_leads" PRIMARY KEY ("id")
)
`);

await queryRunner.query(`
DO $$ BEGIN
ALTER TABLE "dra_lead_generator_leads"
ADD CONSTRAINT "FK_dra_lead_generator_leads_generator"
FOREIGN KEY ("lead_generator_id")
REFERENCES "dra_lead_generators"("id")
ON DELETE CASCADE
ON UPDATE NO ACTION;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE IF EXISTS "dra_lead_generator_leads"`);
await queryRunner.query(`DROP INDEX IF EXISTS "UQ_dra_lead_generators_slug"`);
await queryRunner.query(`DROP TABLE IF EXISTS "dra_lead_generators"`);
}
}
51 changes: 51 additions & 0 deletions backend/src/models/DRALeadGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
Column,
CreateDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { DRALeadGeneratorLead } from './DRALeadGeneratorLead.js';

@Entity('dra_lead_generators')
export class DRALeadGenerator {
@PrimaryGeneratedColumn()
id!: number;

@Column({ type: 'varchar', length: 255 })
title!: string;

@Column({ type: 'varchar', length: 255, unique: true })
slug!: string;

@Column({ type: 'text', nullable: true })
description!: string | null;

@Column({ type: 'varchar', length: 500 })
file_name!: string;

@Column({ type: 'varchar', length: 500 })
original_file_name!: string;

@Column({ type: 'boolean', default: true })
is_gated!: boolean;

@Column({ type: 'boolean', default: true })
is_active!: boolean;

@Column({ type: 'int', default: 0 })
view_count!: number;

@Column({ type: 'int', default: 0 })
download_count!: number;

@CreateDateColumn({ type: 'timestamptz' })
created_at!: Date;

@UpdateDateColumn({ type: 'timestamptz' })
updated_at!: Date;

@OneToMany(() => DRALeadGeneratorLead, (lead) => lead.lead_generator)
leads!: DRALeadGeneratorLead[];
}
44 changes: 44 additions & 0 deletions backend/src/models/DRALeadGeneratorLead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
Relation,
} from 'typeorm';
import { DRALeadGenerator } from './DRALeadGenerator.js';

@Entity('dra_lead_generator_leads')
export class DRALeadGeneratorLead {
@PrimaryGeneratedColumn()
id!: number;

@Column({ type: 'int' })
lead_generator_id!: number;

@Column({ type: 'varchar', length: 255, nullable: true })
full_name!: string | null;

@Column({ type: 'varchar', length: 255 })
email!: string;

@Column({ type: 'varchar', length: 255, nullable: true })
company!: string | null;

@Column({ type: 'varchar', length: 50, nullable: true })
phone!: string | null;

@Column({ type: 'varchar', length: 255, nullable: true })
job_title!: string | null;

@Column({ type: 'varchar', length: 45, nullable: true })
ip_address!: string | null;

@CreateDateColumn({ type: 'timestamptz' })
created_at!: Date;

@ManyToOne(() => DRALeadGenerator, (lg) => lg.leads, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'lead_generator_id' })
lead_generator!: Relation<DRALeadGenerator>;
}
Loading
Loading