contravaulthvnkz / backend /database_schema.sql
jackmichael's picture
Initial commit: RFP Engineering Platform with Deep Research, Inngest integration, and Cloud-ready configurations
7ba65c9
-- =============================================================================
-- 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
-- =============================================================================