-- ============================================================================= -- CONTRAVAULT DATABASE SCHEMA -- ============================================================================= -- This SQL file contains all table definitions for the Contravault platform. -- Run this after enabling the pgvector extension in Neon: -- CREATE EXTENSION IF NOT EXISTS vector; -- CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- ============================================================================= -- Enable extensions CREATE EXTENSION IF NOT EXISTS vector; CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- ============================================================================= -- 1. USERS & AUTHENTICATION -- ============================================================================= CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), email VARCHAR UNIQUE NOT NULL, password_hash VARCHAR, first_name VARCHAR, last_name VARCHAR, display_name VARCHAR, avatar_url VARCHAR, title VARCHAR, department VARCHAR, phone VARCHAR, role VARCHAR DEFAULT 'user', is_active BOOLEAN DEFAULT TRUE, is_verified BOOLEAN DEFAULT FALSE, preferences JSONB DEFAULT '{}', timezone VARCHAR DEFAULT 'UTC', created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), last_login TIMESTAMPTZ ); CREATE INDEX idx_users_email ON users(email); CREATE TABLE IF NOT EXISTS teams ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR NOT NULL, description TEXT, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS team_members ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, role VARCHAR DEFAULT 'member', joined_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(team_id, user_id) ); -- ============================================================================= -- 2. OPPORTUNITIES (SAM.gov Discovery) -- ============================================================================= CREATE TABLE IF NOT EXISTS opportunities ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), solicitation_number VARCHAR UNIQUE NOT NULL, notice_id VARCHAR, title VARCHAR NOT NULL, agency VARCHAR, sub_agency VARCHAR, office VARCHAR, posted_date TIMESTAMPTZ, response_deadline TIMESTAMPTZ, archive_date TIMESTAMPTZ, description TEXT, description_plain TEXT, naics_code VARCHAR, naics_description VARCHAR, psc_code VARCHAR, set_aside VARCHAR, place_of_performance VARCHAR, contract_type VARCHAR, estimated_value FLOAT, pop_start TIMESTAMPTZ, pop_end TIMESTAMPTZ, link VARCHAR, resource_links JSONB, fit_score INTEGER DEFAULT 0, fit_score_breakdown JSONB, ai_summary TEXT, ai_tags VARCHAR[], status VARCHAR DEFAULT 'New', ignore_reason VARCHAR, notes TEXT, discovered_at TIMESTAMPTZ DEFAULT NOW(), last_checked TIMESTAMPTZ, change_history JSONB, project_id UUID ); CREATE INDEX idx_opportunities_solicitation ON opportunities(solicitation_number); CREATE INDEX idx_opportunities_notice ON opportunities(notice_id); CREATE INDEX idx_opportunities_agency ON opportunities(agency); CREATE INDEX idx_opportunities_deadline ON opportunities(response_deadline); CREATE INDEX idx_opportunities_status ON opportunities(status); CREATE INDEX idx_opportunities_naics ON opportunities(naics_code); CREATE INDEX idx_opportunities_setaside ON opportunities(set_aside); CREATE INDEX idx_opportunities_deadline_status ON opportunities(response_deadline, status); CREATE INDEX idx_opportunities_naics_setaside ON opportunities(naics_code, set_aside); -- ============================================================================= -- 3. PROJECTS -- ============================================================================= CREATE TABLE IF NOT EXISTS projects ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), short_id VARCHAR UNIQUE, opportunity_id UUID REFERENCES opportunities(id) ON DELETE SET NULL, name VARCHAR NOT NULL, client VARCHAR, solicitation_number VARCHAR, contract_number VARCHAR, description TEXT, executive_summary TEXT, due_date TIMESTAMPTZ, questions_due TIMESTAMPTZ, award_date TIMESTAMPTZ, pop_start TIMESTAMPTZ, pop_end TIMESTAMPTZ, estimated_value FLOAT, bid_amount FLOAT, budget_allocated FLOAT, naics_code VARCHAR, set_aside VARCHAR, contract_type VARCHAR, status VARCHAR DEFAULT 'Created', stage VARCHAR DEFAULT 'discovery', health_score INTEGER DEFAULT 100, go_no_go_decision VARCHAR, go_no_go_date TIMESTAMPTZ, go_no_go_notes TEXT, owner_id UUID REFERENCES users(id) ON DELETE SET NULL, capture_manager VARCHAR, proposal_manager VARCHAR, tags VARCHAR[], custom_fields JSONB, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), archived_at TIMESTAMPTZ ); CREATE INDEX idx_projects_short_id ON projects(short_id); CREATE INDEX idx_projects_solicitation ON projects(solicitation_number); CREATE INDEX idx_projects_due_date ON projects(due_date); CREATE INDEX idx_projects_status ON projects(status); CREATE INDEX idx_projects_status_due ON projects(status, due_date); -- Add FK from opportunities back to projects ALTER TABLE opportunities ADD CONSTRAINT fk_opportunities_project FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE SET NULL; -- Project team members (many-to-many) CREATE TABLE IF NOT EXISTS project_team_members ( project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, role VARCHAR DEFAULT 'member', added_at TIMESTAMPTZ DEFAULT NOW(), PRIMARY KEY (project_id, user_id) ); -- ============================================================================= -- 4. DOCUMENTS -- ============================================================================= CREATE TABLE IF NOT EXISTS documents ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, filename VARCHAR NOT NULL, original_filename VARCHAR, path VARCHAR NOT NULL, s3_key VARCHAR, file_type VARCHAR, mime_type VARCHAR, size_bytes BIGINT, page_count INTEGER, word_count INTEGER, category VARCHAR DEFAULT 'Other', subcategory VARCHAR, section_number VARCHAR, is_ingested BOOLEAN DEFAULT FALSE, is_shredded BOOLEAN DEFAULT FALSE, is_indexed BOOLEAN DEFAULT FALSE, ingestion_status VARCHAR DEFAULT 'pending', ingestion_error TEXT, ingestion_started_at TIMESTAMPTZ, ingestion_completed_at TIMESTAMPTZ, extracted_text TEXT, extracted_metadata JSONB, structure JSONB, md5_hash VARCHAR, sha256_hash VARCHAR, uploaded_by UUID REFERENCES users(id) ON DELETE SET NULL, uploaded_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_documents_project ON documents(project_id); CREATE INDEX idx_documents_md5 ON documents(md5_hash); CREATE INDEX idx_documents_project_category ON documents(project_id, category); CREATE TABLE IF NOT EXISTS document_versions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE, version_number INTEGER NOT NULL, path VARCHAR NOT NULL, s3_key VARCHAR, size_bytes BIGINT, md5_hash VARCHAR, change_summary TEXT, uploaded_by UUID REFERENCES users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(document_id, version_number) ); -- ============================================================================= -- 5. DOCUMENT CHUNKS (Vector Store for RAG) -- ============================================================================= CREATE TABLE IF NOT EXISTS doc_chunks ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE, content TEXT NOT NULL, content_tokens INTEGER, chunk_index INTEGER, page_number INTEGER, section_ref VARCHAR, parent_chunk_id UUID REFERENCES doc_chunks(id) ON DELETE SET NULL, context_before TEXT, context_after TEXT, chunk_type VARCHAR, metadata JSONB, embedding vector(1536), -- pgvector for OpenAI embeddings embedding_model VARCHAR DEFAULT 'text-embedding-3-small', created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_chunks_document ON doc_chunks(document_id); CREATE INDEX idx_chunks_document_page ON doc_chunks(document_id, page_number); -- Vector similarity index (for semantic search) CREATE INDEX idx_chunks_embedding ON doc_chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100); -- ============================================================================= -- 6. TAGS -- ============================================================================= CREATE TABLE IF NOT EXISTS tags ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR UNIQUE NOT NULL, color VARCHAR DEFAULT '#3b82f6', category VARCHAR, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS requirement_tags ( requirement_id UUID NOT NULL, tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE, PRIMARY KEY (requirement_id, tag_id) ); CREATE TABLE IF NOT EXISTS document_tags ( document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE, tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE, PRIMARY KEY (document_id, tag_id) ); -- ============================================================================= -- 7. REQUIREMENTS (Shredder Output / Compliance Matrix) -- ============================================================================= CREATE TABLE IF NOT EXISTS requirements ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, document_id UUID REFERENCES documents(id) ON DELETE SET NULL, short_id VARCHAR, section_ref VARCHAR, requirement_text TEXT NOT NULL, source_text TEXT, category VARCHAR, subcategory VARCHAR, type VARCHAR DEFAULT 'Mandatory', volume VARCHAR, page_number INTEGER, line_number INTEGER, risk_level VARCHAR DEFAULT 'Low', risk_score INTEGER DEFAULT 0, risk_factors JSONB, status VARCHAR DEFAULT 'NotStarted', compliance_response TEXT, evidence TEXT, exceptions TEXT, assigned_to UUID REFERENCES users(id) ON DELETE SET NULL, assigned_by UUID REFERENCES users(id) ON DELETE SET NULL, assigned_at TIMESTAMPTZ, due_date TIMESTAMPTZ, priority VARCHAR DEFAULT 'medium', reviewed_by UUID REFERENCES users(id) ON DELETE SET NULL, reviewed_at TIMESTAMPTZ, ai_suggestion TEXT, ai_confidence FLOAT, notes TEXT, internal_notes TEXT, parent_id UUID REFERENCES requirements(id) ON DELETE SET NULL, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_requirements_project ON requirements(project_id); CREATE INDEX idx_requirements_short_id ON requirements(short_id); CREATE INDEX idx_requirements_project_status ON requirements(project_id, status); CREATE INDEX idx_requirements_assigned ON requirements(assigned_to, status); -- Add FK for requirement_tags ALTER TABLE requirement_tags ADD CONSTRAINT fk_requirement_tags_requirement FOREIGN KEY (requirement_id) REFERENCES requirements(id) ON DELETE CASCADE; CREATE TABLE IF NOT EXISTS requirement_responses ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), requirement_id UUID NOT NULL REFERENCES requirements(id) ON DELETE CASCADE, version INTEGER DEFAULT 1, response_text TEXT, evidence TEXT, status VARCHAR DEFAULT 'draft', submitted_by UUID REFERENCES users(id) ON DELETE SET NULL, submitted_at TIMESTAMPTZ, reviewed_by UUID REFERENCES users(id) ON DELETE SET NULL, reviewed_at TIMESTAMPTZ, review_notes TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(requirement_id, version) ); -- ============================================================================= -- 8. PROPOSAL SECTIONS -- ============================================================================= CREATE TABLE IF NOT EXISTS proposal_sections ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, parent_id UUID REFERENCES proposal_sections(id) ON DELETE SET NULL, order_index INTEGER DEFAULT 0, title VARCHAR NOT NULL, section_number VARCHAR, volume VARCHAR, content_sfdt JSONB, content_html TEXT, content_markdown TEXT, content_plain TEXT, word_count INTEGER DEFAULT 0, page_estimate FLOAT, status VARCHAR DEFAULT 'draft', is_locked BOOLEAN DEFAULT FALSE, locked_by UUID REFERENCES users(id) ON DELETE SET NULL, locked_at TIMESTAMPTZ, assigned_to UUID REFERENCES users(id) ON DELETE SET NULL, due_date TIMESTAMPTZ, page_limit INTEGER, is_over_limit BOOLEAN DEFAULT FALSE, ai_generated BOOLEAN DEFAULT FALSE, ai_generation_prompt TEXT, ai_model_used VARCHAR, version INTEGER DEFAULT 1, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), last_edited_by UUID REFERENCES users(id) ON DELETE SET NULL ); CREATE INDEX idx_sections_project ON proposal_sections(project_id); CREATE INDEX idx_sections_project_volume ON proposal_sections(project_id, volume); CREATE TABLE IF NOT EXISTS proposal_section_versions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), section_id UUID NOT NULL REFERENCES proposal_sections(id) ON DELETE CASCADE, version_number INTEGER NOT NULL, content_sfdt JSONB, content_html TEXT, word_count INTEGER, change_summary TEXT, edited_by UUID REFERENCES users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(section_id, version_number) ); CREATE TABLE IF NOT EXISTS proposal_templates ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR NOT NULL, description TEXT, category VARCHAR, content_sfdt JSONB, content_html TEXT, use_count INTEGER DEFAULT 0, tags VARCHAR[], is_public BOOLEAN DEFAULT FALSE, created_by UUID REFERENCES users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- ============================================================================= -- 9. PRICING -- ============================================================================= CREATE TABLE IF NOT EXISTS pricing_items ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, clin_number VARCHAR, clin_description TEXT, parent_clin_id UUID REFERENCES pricing_items(id) ON DELETE SET NULL, quantity FLOAT, unit VARCHAR, unit_price FLOAT, extended_price FLOAT, labor_cost FLOAT, material_cost FLOAT, odc_cost FLOAT, indirect_cost FLOAT, profit FLOAT, labor_category VARCHAR, labor_hours FLOAT, labor_rate FLOAT, period VARCHAR, start_date TIMESTAMPTZ, end_date TIMESTAMPTZ, notes TEXT, assumptions TEXT, order_index INTEGER DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS labor_categories ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR NOT NULL, description TEXT, hourly_rate FLOAT, annual_salary FLOAT, burden_rate FLOAT, education_requirement VARCHAR, experience_years INTEGER, clearance_level VARCHAR, gsa_rate FLOAT, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW() ); -- ============================================================================= -- 10. TASKS & WORKFLOW -- ============================================================================= CREATE TABLE IF NOT EXISTS tasks ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID REFERENCES projects(id) ON DELETE CASCADE, title VARCHAR NOT NULL, description TEXT, status VARCHAR DEFAULT 'todo', priority VARCHAR DEFAULT 'medium', assignee_id UUID REFERENCES users(id) ON DELETE SET NULL, created_by UUID REFERENCES users(id) ON DELETE SET NULL, due_date TIMESTAMPTZ, started_at TIMESTAMPTZ, completed_at TIMESTAMPTZ, requirement_id UUID REFERENCES requirements(id) ON DELETE SET NULL, section_id UUID REFERENCES proposal_sections(id) ON DELETE SET NULL, estimated_hours FLOAT, actual_hours FLOAT, tags VARCHAR[], created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS comments ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID REFERENCES projects(id) ON DELETE CASCADE, requirement_id UUID REFERENCES requirements(id) ON DELETE CASCADE, section_id UUID REFERENCES proposal_sections(id) ON DELETE CASCADE, task_id UUID REFERENCES tasks(id) ON DELETE CASCADE, parent_id UUID REFERENCES comments(id) ON DELETE CASCADE, content TEXT NOT NULL, content_html TEXT, user_id UUID REFERENCES users(id) ON DELETE SET NULL, mentions UUID[], is_resolved BOOLEAN DEFAULT FALSE, resolved_by UUID REFERENCES users(id) ON DELETE SET NULL, resolved_at TIMESTAMPTZ, is_edited BOOLEAN DEFAULT FALSE, edited_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS notifications ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, type VARCHAR DEFAULT 'info', title VARCHAR NOT NULL, message TEXT, entity_type VARCHAR, entity_id UUID, action_url VARCHAR, is_read BOOLEAN DEFAULT FALSE, read_at TIMESTAMPTZ, email_sent BOOLEAN DEFAULT FALSE, email_sent_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), expires_at TIMESTAMPTZ ); -- ============================================================================= -- 11. AI & KNOWLEDGE -- ============================================================================= CREATE TABLE IF NOT EXISTS ai_conversations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID REFERENCES projects(id) ON DELETE CASCADE, user_id UUID REFERENCES users(id) ON DELETE SET NULL, title VARCHAR, context_type VARCHAR, context_id UUID, model VARCHAR DEFAULT 'gpt-4', temperature FLOAT DEFAULT 0.7, system_prompt TEXT, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS ai_messages ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), conversation_id UUID NOT NULL REFERENCES ai_conversations(id) ON DELETE CASCADE, role VARCHAR NOT NULL, content TEXT NOT NULL, prompt_tokens INTEGER, completion_tokens INTEGER, total_tokens INTEGER, retrieved_chunks JSONB, rating INTEGER, feedback TEXT, model VARCHAR, latency_ms INTEGER, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS ai_generation_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID REFERENCES projects(id) ON DELETE SET NULL, user_id UUID REFERENCES users(id) ON DELETE SET NULL, generation_type VARCHAR, prompt TEXT, output TEXT, provider VARCHAR, model VARCHAR, temperature FLOAT, prompt_tokens INTEGER, completion_tokens INTEGER, total_tokens INTEGER, cost_usd FLOAT, latency_ms INTEGER, was_accepted BOOLEAN, user_edits TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS company_knowledge ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), category VARCHAR NOT NULL, subcategory VARCHAR, title VARCHAR NOT NULL, content TEXT NOT NULL, metadata JSONB, tags VARCHAR[], embedding vector(1536), is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Vector index for company knowledge CREATE INDEX idx_company_knowledge_embedding ON company_knowledge USING ivfflat (embedding vector_cosine_ops) WITH (lists = 50); -- ============================================================================= -- 12. ACTIVITY & AUDIT LOGS -- ============================================================================= CREATE TABLE IF NOT EXISTS user_sessions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, session_token VARCHAR UNIQUE, ip_address INET, user_agent TEXT, device_type VARCHAR, browser VARCHAR, os VARCHAR, country VARCHAR, city VARCHAR, started_at TIMESTAMPTZ DEFAULT NOW(), last_activity TIMESTAMPTZ DEFAULT NOW(), ended_at TIMESTAMPTZ, is_active BOOLEAN DEFAULT TRUE, logout_reason VARCHAR ); CREATE INDEX idx_sessions_token ON user_sessions(session_token); CREATE INDEX idx_sessions_user ON user_sessions(user_id); CREATE TABLE IF NOT EXISTS activity_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, session_id UUID REFERENCES user_sessions(id) ON DELETE SET NULL, activity_type VARCHAR NOT NULL, action VARCHAR, entity_type VARCHAR, entity_id UUID, entity_name VARCHAR, project_id UUID REFERENCES projects(id) ON DELETE SET NULL, description TEXT, details JSONB, old_values JSONB, new_values JSONB, ip_address INET, user_agent VARCHAR, request_id VARCHAR, status VARCHAR DEFAULT 'success', error_message TEXT, duration_ms INTEGER, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_activity_type ON activity_logs(activity_type); CREATE INDEX idx_activity_entity ON activity_logs(entity_type, entity_id); CREATE INDEX idx_activity_project ON activity_logs(project_id); CREATE INDEX idx_activity_user_time ON activity_logs(user_id, created_at); CREATE INDEX idx_activity_project_time ON activity_logs(project_id, created_at); CREATE INDEX idx_activity_type_time ON activity_logs(activity_type, created_at); CREATE INDEX idx_activity_created ON activity_logs(created_at); CREATE TABLE IF NOT EXISTS audit_trails ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID, -- No FK to preserve data if user deleted user_email VARCHAR, action VARCHAR NOT NULL, table_name VARCHAR NOT NULL, record_id UUID, old_data JSONB, new_data JSONB, changed_fields VARCHAR[], reason TEXT, ip_address INET, user_agent TEXT, request_path VARCHAR, request_method VARCHAR, timestamp TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_audit_table ON audit_trails(table_name); CREATE INDEX idx_audit_record ON audit_trails(record_id); CREATE INDEX idx_audit_table_record ON audit_trails(table_name, record_id); CREATE INDEX idx_audit_user_time ON audit_trails(user_id, timestamp); CREATE INDEX idx_audit_timestamp ON audit_trails(timestamp); CREATE TABLE IF NOT EXISTS error_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, project_id UUID REFERENCES projects(id) ON DELETE SET NULL, error_type VARCHAR NOT NULL, error_message TEXT NOT NULL, stack_trace TEXT, request_path VARCHAR, request_method VARCHAR, request_body JSONB, environment VARCHAR, server_hostname VARCHAR, is_resolved BOOLEAN DEFAULT FALSE, resolved_by UUID, resolved_at TIMESTAMPTZ, resolution_notes TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_errors_created ON error_logs(created_at); -- ============================================================================= -- 13. ANALYTICS -- ============================================================================= CREATE TABLE IF NOT EXISTS page_views ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, session_id UUID REFERENCES user_sessions(id) ON DELETE SET NULL, page_path VARCHAR NOT NULL, page_title VARCHAR, project_id UUID REFERENCES projects(id) ON DELETE SET NULL, referrer VARCHAR, time_on_page_ms INTEGER, viewport_width INTEGER, viewport_height INTEGER, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_pageview_path ON page_views(page_path); CREATE INDEX idx_pageview_user_time ON page_views(user_id, created_at); CREATE INDEX idx_pageview_page_time ON page_views(page_path, created_at); CREATE INDEX idx_pageview_created ON page_views(created_at); CREATE TABLE IF NOT EXISTS search_history ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, project_id UUID REFERENCES projects(id) ON DELETE SET NULL, query VARCHAR NOT NULL, search_type VARCHAR, filters JSONB, result_count INTEGER, clicked_results JSONB, latency_ms INTEGER, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS feature_usage ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, project_id UUID REFERENCES projects(id) ON DELETE SET NULL, feature_name VARCHAR NOT NULL, action VARCHAR, metadata JSONB, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_feature_name ON feature_usage(feature_name); CREATE INDEX idx_feature_name_time ON feature_usage(feature_name, created_at); -- ============================================================================= -- 14. SETTINGS -- ============================================================================= CREATE TABLE IF NOT EXISTS system_settings ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), key VARCHAR UNIQUE NOT NULL, value JSONB, value_type VARCHAR, category VARCHAR, description TEXT, is_secret BOOLEAN DEFAULT FALSE, is_editable BOOLEAN DEFAULT TRUE, updated_by UUID REFERENCES users(id) ON DELETE SET NULL, updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_settings_key ON system_settings(key); CREATE TABLE IF NOT EXISTS user_settings ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, key VARCHAR NOT NULL, value JSONB, updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(user_id, key) ); CREATE TABLE IF NOT EXISTS pursuit_policies ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), parameter_name VARCHAR UNIQUE NOT NULL, threshold_value VARCHAR NOT NULL, comparison_operator VARCHAR, weight FLOAT DEFAULT 1.0, category VARCHAR, description TEXT, is_mandatory BOOLEAN DEFAULT FALSE, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- ============================================================================= -- 15. SUBMISSION -- ============================================================================= CREATE TABLE IF NOT EXISTS submission_packages ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, name VARCHAR NOT NULL, version INTEGER DEFAULT 1, status VARCHAR DEFAULT 'draft', submission_method VARCHAR, submitted_at TIMESTAMPTZ, submitted_by UUID REFERENCES users(id) ON DELETE SET NULL, confirmation_number VARCHAR, is_validated BOOLEAN DEFAULT FALSE, validation_errors JSONB, is_sanitized BOOLEAN DEFAULT FALSE, sanitization_report JSONB, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS submission_files ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), package_id UUID NOT NULL REFERENCES submission_packages(id) ON DELETE CASCADE, filename VARCHAR NOT NULL, path VARCHAR NOT NULL, s3_key VARCHAR, file_type VARCHAR, size_bytes BIGINT, volume VARCHAR, order_index INTEGER DEFAULT 0, page_count INTEGER, word_count INTEGER, is_compliant BOOLEAN, md5_hash VARCHAR, sha256_hash VARCHAR, created_at TIMESTAMPTZ DEFAULT NOW() ); -- ============================================================================= -- 16. ANALYSIS & INTELLIGENCE -- ============================================================================= CREATE TABLE IF NOT EXISTS analysis_results ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, analysis_type VARCHAR NOT NULL, result_json JSONB NOT NULL, overall_score FLOAT, confidence FLOAT, model_used VARCHAR, status VARCHAR DEFAULT 'complete', error_message TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), created_by UUID REFERENCES users(id) ON DELETE SET NULL ); CREATE INDEX idx_analysis_project ON analysis_results(project_id); CREATE INDEX idx_analysis_type ON analysis_results(analysis_type); CREATE TABLE IF NOT EXISTS competitors ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR NOT NULL, duns_number VARCHAR, cage_code VARCHAR, website VARCHAR, description TEXT, capabilities JSONB, proprietary_technologies JSONB, certifications JSONB, contract_vehicles JSONB, past_performance_agencies JSONB, incumbent_contracts JSONB, strengths JSONB, weaknesses JSONB, annual_revenue FLOAT, employee_count INTEGER, size_standard VARCHAR, socioeconomic_status JSONB, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_competitors_name ON competitors(name); CREATE TABLE IF NOT EXISTS competitor_encounters ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), competitor_id UUID NOT NULL REFERENCES competitors(id) ON DELETE CASCADE, project_id UUID REFERENCES projects(id) ON DELETE CASCADE, opportunity_id UUID REFERENCES opportunities(id) ON DELETE CASCADE, role VARCHAR, confidence FLOAT, source VARCHAR, outcome VARCHAR, notes TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); -- ============================================================================= -- 17. INTEGRATIONS -- ============================================================================= CREATE TABLE IF NOT EXISTS integration_configs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR UNIQUE NOT NULL, provider VARCHAR NOT NULL, config JSONB, credentials JSONB, is_enabled BOOLEAN DEFAULT TRUE, last_sync TIMESTAMPTZ, last_error TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS webhook_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), direction VARCHAR NOT NULL, provider VARCHAR, event_type VARCHAR, url VARCHAR, method VARCHAR, headers JSONB, payload JSONB, status_code INTEGER, response_body JSONB, is_success BOOLEAN, error_message TEXT, retry_count INTEGER DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS scheduled_jobs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR NOT NULL, job_type VARCHAR NOT NULL, cron_expression VARCHAR, interval_minutes INTEGER, config JSONB, is_enabled BOOLEAN DEFAULT TRUE, last_run TIMESTAMPTZ, last_status VARCHAR, last_error TEXT, next_run TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS job_executions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), job_id UUID REFERENCES scheduled_jobs(id) ON DELETE CASCADE, job_name VARCHAR, started_at TIMESTAMPTZ DEFAULT NOW(), completed_at TIMESTAMPTZ, duration_ms INTEGER, status VARCHAR, error_message TEXT, result JSONB, records_processed INTEGER ); -- ============================================================================= -- END OF SCHEMA -- =============================================================================