feat: Add Google Analytics integration and fix anonymous tracking

- Add Google Analytics tracking (G-ZDZ26XYN2P) to frontend
- Create comprehensive analytics utility with event tracking
- Track URL submissions, analysis results, and user authentication
- Add route tracking for SPA navigation
- Fix CORS configuration to support both localhost and production
- Fix home page tracking form to display results instead of auto-redirect
- Add service management scripts for easier deployment
- Update database migrations for enhanced analysis features

Key Features:
- Anonymous and authenticated user tracking
- SSL/SEO/Security analysis event tracking
- Error tracking for debugging
- Page view tracking for SPA routes
- Multi-origin CORS support for development and production
This commit is contained in:
Andrei
2025-08-23 17:45:01 +00:00
parent 58f8093689
commit 6e41d9874d
19 changed files with 1904 additions and 95 deletions

View File

@@ -0,0 +1,264 @@
-- CreateEnum
CREATE TYPE "Role" AS ENUM ('OWNER', 'ADMIN', 'MEMBER');
-- CreateEnum
CREATE TYPE "CheckStatus" AS ENUM ('OK', 'ERROR', 'TIMEOUT', 'LOOP');
-- CreateEnum
CREATE TYPE "RedirectType" AS ENUM ('HTTP_301', 'HTTP_302', 'HTTP_307', 'HTTP_308', 'META_REFRESH', 'JS', 'FINAL', 'OTHER');
-- CreateEnum
CREATE TYPE "MixedContent" AS ENUM ('NONE', 'PRESENT', 'FINAL_TO_HTTP');
-- CreateEnum
CREATE TYPE "JobStatus" AS ENUM ('PENDING', 'QUEUED', 'RUNNING', 'COMPLETED', 'FAILED', 'CANCELLED', 'ERROR');
-- CreateTable
CREATE TABLE "users" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT NOT NULL,
"password_hash" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"last_login_at" TIMESTAMP(3),
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "organizations" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"plan" TEXT NOT NULL DEFAULT 'free',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "organizations_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "org_memberships" (
"id" TEXT NOT NULL,
"org_id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"role" "Role" NOT NULL,
CONSTRAINT "org_memberships_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "projects" (
"id" TEXT NOT NULL,
"org_id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"settings_json" JSONB NOT NULL DEFAULT '{}',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "projects_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "checks" (
"id" TEXT NOT NULL,
"project_id" TEXT NOT NULL,
"input_url" TEXT NOT NULL,
"method" TEXT NOT NULL DEFAULT 'GET',
"headers_json" JSONB NOT NULL DEFAULT '{}',
"user_agent" TEXT,
"started_at" TIMESTAMP(3) NOT NULL,
"finished_at" TIMESTAMP(3),
"status" "CheckStatus" NOT NULL,
"final_url" TEXT,
"total_time_ms" INTEGER,
"report_id" TEXT,
CONSTRAINT "checks_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "hops" (
"id" TEXT NOT NULL,
"check_id" TEXT NOT NULL,
"hop_index" INTEGER NOT NULL,
"url" TEXT NOT NULL,
"scheme" TEXT,
"status_code" INTEGER,
"redirect_type" "RedirectType" NOT NULL,
"latency_ms" INTEGER,
"content_type" TEXT,
"reason" TEXT,
"response_headers_json" JSONB NOT NULL DEFAULT '{}',
CONSTRAINT "hops_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ssl_inspections" (
"id" TEXT NOT NULL,
"check_id" TEXT NOT NULL,
"host" TEXT NOT NULL,
"valid_from" TIMESTAMP(3),
"valid_to" TIMESTAMP(3),
"days_to_expiry" INTEGER,
"issuer" TEXT,
"protocol" TEXT,
"warnings_json" JSONB NOT NULL DEFAULT '[]',
CONSTRAINT "ssl_inspections_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "seo_flags" (
"id" TEXT NOT NULL,
"check_id" TEXT NOT NULL,
"robots_txt_status" TEXT,
"robots_txt_rules_json" JSONB NOT NULL DEFAULT '{}',
"meta_robots" TEXT,
"canonical_url" TEXT,
"sitemap_present" BOOLEAN NOT NULL DEFAULT false,
"noindex" BOOLEAN NOT NULL DEFAULT false,
"nofollow" BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT "seo_flags_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "security_flags" (
"id" TEXT NOT NULL,
"check_id" TEXT NOT NULL,
"safe_browsing_status" TEXT,
"mixed_content" "MixedContent" NOT NULL DEFAULT 'NONE',
"https_to_http" BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT "security_flags_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "reports" (
"id" TEXT NOT NULL,
"check_id" TEXT NOT NULL,
"markdown_path" TEXT,
"pdf_path" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "reports_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "bulk_jobs" (
"id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"organization_id" TEXT,
"project_id" TEXT NOT NULL,
"upload_path" TEXT NOT NULL,
"status" "JobStatus" NOT NULL,
"total_urls" INTEGER NOT NULL DEFAULT 0,
"processed_urls" INTEGER NOT NULL DEFAULT 0,
"successful_urls" INTEGER NOT NULL DEFAULT 0,
"failed_urls" INTEGER NOT NULL DEFAULT 0,
"config_json" JSONB NOT NULL DEFAULT '{}',
"urls_json" JSONB,
"results_json" JSONB,
"progress_json" JSONB NOT NULL DEFAULT '{}',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"started_at" TIMESTAMP(3),
"finished_at" TIMESTAMP(3),
"completed_at" TIMESTAMP(3),
CONSTRAINT "bulk_jobs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "api_keys" (
"id" TEXT NOT NULL,
"org_id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"token_hash" TEXT NOT NULL,
"perms_json" JSONB NOT NULL DEFAULT '{}',
"rate_limit_quota" INTEGER NOT NULL DEFAULT 1000,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "api_keys_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "audit_logs" (
"id" TEXT NOT NULL,
"org_id" TEXT NOT NULL,
"actor_user_id" TEXT,
"action" TEXT NOT NULL,
"entity" TEXT NOT NULL,
"entity_id" TEXT NOT NULL,
"meta_json" JSONB NOT NULL DEFAULT '{}',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
-- CreateIndex
CREATE UNIQUE INDEX "org_memberships_org_id_user_id_key" ON "org_memberships"("org_id", "user_id");
-- CreateIndex
CREATE INDEX "checks_project_id_started_at_idx" ON "checks"("project_id", "started_at" DESC);
-- CreateIndex
CREATE INDEX "hops_check_id_hop_index_idx" ON "hops"("check_id", "hop_index");
-- CreateIndex
CREATE UNIQUE INDEX "seo_flags_check_id_key" ON "seo_flags"("check_id");
-- CreateIndex
CREATE UNIQUE INDEX "security_flags_check_id_key" ON "security_flags"("check_id");
-- CreateIndex
CREATE UNIQUE INDEX "api_keys_token_hash_key" ON "api_keys"("token_hash");
-- CreateIndex
CREATE INDEX "api_keys_token_hash_idx" ON "api_keys"("token_hash");
-- AddForeignKey
ALTER TABLE "org_memberships" ADD CONSTRAINT "org_memberships_org_id_fkey" FOREIGN KEY ("org_id") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "org_memberships" ADD CONSTRAINT "org_memberships_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "projects" ADD CONSTRAINT "projects_org_id_fkey" FOREIGN KEY ("org_id") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "checks" ADD CONSTRAINT "checks_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "hops" ADD CONSTRAINT "hops_check_id_fkey" FOREIGN KEY ("check_id") REFERENCES "checks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ssl_inspections" ADD CONSTRAINT "ssl_inspections_check_id_fkey" FOREIGN KEY ("check_id") REFERENCES "checks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "seo_flags" ADD CONSTRAINT "seo_flags_check_id_fkey" FOREIGN KEY ("check_id") REFERENCES "checks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "security_flags" ADD CONSTRAINT "security_flags_check_id_fkey" FOREIGN KEY ("check_id") REFERENCES "checks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "reports" ADD CONSTRAINT "reports_check_id_fkey" FOREIGN KEY ("check_id") REFERENCES "checks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "bulk_jobs" ADD CONSTRAINT "bulk_jobs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "bulk_jobs" ADD CONSTRAINT "bulk_jobs_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "bulk_jobs" ADD CONSTRAINT "bulk_jobs_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "api_keys" ADD CONSTRAINT "api_keys_org_id_fkey" FOREIGN KEY ("org_id") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_org_id_fkey" FOREIGN KEY ("org_id") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_actor_user_id_fkey" FOREIGN KEY ("actor_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"