Skip to content

Commit 3f8cf85

Browse files
committed
2 parents 174e99e + ef74b74 commit 3f8cf85

12 files changed

Lines changed: 1150 additions & 700 deletions
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
-- ====================================================================
2+
-- Facial Recognition System - Database Schema
3+
-- ====================================================================
4+
-- This schema supports the facial recognition feature for CrimeLinkAnalyzer
5+
-- Created: December 11, 2025
6+
-- ====================================================================
7+
8+
-- Enable required PostgreSQL extension for EXCLUDE constraints
9+
-- The btree_gist extension is required for using EXCLUDE constraints with equality operators
10+
-- This allows us to enforce "one primary photo per criminal" at the database level
11+
CREATE EXTENSION IF NOT EXISTS btree_gist;
12+
13+
-- Drop existing tables if they exist (for clean setup)
14+
DROP TABLE IF EXISTS facial_recognition_logs CASCADE;
15+
DROP TABLE IF EXISTS suspect_photos CASCADE;
16+
DROP TABLE IF EXISTS criminals CASCADE;
17+
18+
-- ====================================================================
19+
-- CRIMINALS TABLE
20+
-- ====================================================================
21+
-- Stores criminal records with biometric data
22+
CREATE TABLE criminals (
23+
criminal_id SERIAL PRIMARY KEY,
24+
name VARCHAR(255) NOT NULL,
25+
nic VARCHAR(20) UNIQUE,
26+
alias VARCHAR(255),
27+
date_of_birth DATE,
28+
gender VARCHAR(10) CHECK (gender IN ('Male', 'Female', 'Other')),
29+
address TEXT,
30+
nationality VARCHAR(100) DEFAULT 'Sri Lankan',
31+
32+
-- Crime information stored as JSONB for flexibility
33+
crime_history JSONB DEFAULT '[]'::jsonb,
34+
35+
-- Primary photo reference
36+
primary_photo_url VARCHAR(500),
37+
38+
-- Face embedding - stored as BYTEA (binary)
39+
-- This is the average embedding from all photos
40+
face_embedding BYTEA,
41+
embedding_model VARCHAR(50) DEFAULT 'buffalo_sc',
42+
embedding_dimension INTEGER DEFAULT 512,
43+
44+
-- Status tracking
45+
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'archived')),
46+
risk_level VARCHAR(20) DEFAULT 'medium' CHECK (risk_level IN ('low', 'medium', 'high', 'critical')),
47+
48+
-- Audit fields
49+
created_by INTEGER, -- User ID who created this record
50+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
51+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
52+
53+
-- Metadata
54+
notes TEXT,
55+
last_seen_location VARCHAR(255),
56+
last_seen_date DATE
57+
);
58+
59+
-- ====================================================================
60+
-- SUSPECT_PHOTOS TABLE
61+
-- ====================================================================
62+
-- Stores multiple photos per criminal for better accuracy
63+
CREATE TABLE suspect_photos (
64+
photo_id SERIAL PRIMARY KEY,
65+
criminal_id INTEGER NOT NULL REFERENCES criminals(criminal_id) ON DELETE CASCADE,
66+
67+
-- Photo storage
68+
photo_url VARCHAR(500) NOT NULL,
69+
photo_hash VARCHAR(64) UNIQUE, -- SHA-256 hash to prevent duplicates
70+
file_size_bytes INTEGER,
71+
72+
-- Face detection metadata
73+
face_embedding BYTEA NOT NULL, -- Individual photo embedding
74+
face_confidence DECIMAL(5,2), -- Detection confidence (0-100)
75+
face_bbox JSONB, -- Bounding box coordinates {x, y, width, height}
76+
77+
-- Photo metadata
78+
is_primary BOOLEAN DEFAULT FALSE,
79+
photo_quality VARCHAR(20) CHECK (photo_quality IN ('low', 'medium', 'high', 'excellent')),
80+
image_width INTEGER,
81+
image_height INTEGER,
82+
83+
-- Source tracking
84+
source VARCHAR(100), -- e.g., 'manual_upload', 'cctv', 'arrest_record'
85+
source_date DATE,
86+
87+
-- Audit
88+
uploaded_by INTEGER, -- User ID
89+
uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
90+
91+
-- Constraints
92+
CONSTRAINT only_one_primary_per_criminal
93+
EXCLUDE USING gist (criminal_id WITH =)
94+
WHERE (is_primary = true)
95+
);
96+
97+
-- ====================================================================
98+
-- FACIAL_RECOGNITION_LOGS TABLE
99+
-- ====================================================================
100+
-- Audit trail for all facial recognition requests
101+
CREATE TABLE facial_recognition_logs (
102+
log_id SERIAL PRIMARY KEY,
103+
104+
-- Request details
105+
analysis_type VARCHAR(50) DEFAULT 'suspect_match', -- 'suspect_match', 'criminal_registration'
106+
uploaded_image_url VARCHAR(500),
107+
uploaded_image_hash VARCHAR(64),
108+
109+
-- Face detection results
110+
face_detected BOOLEAN DEFAULT FALSE,
111+
face_count INTEGER DEFAULT 0,
112+
face_quality VARCHAR(20),
113+
114+
-- Matching results
115+
matches_found INTEGER DEFAULT 0,
116+
best_match_criminal_id INTEGER REFERENCES criminals(criminal_id),
117+
best_match_similarity DECIMAL(5,2), -- Percentage (0-100)
118+
match_threshold DECIMAL(5,2) DEFAULT 75.00,
119+
120+
-- All matches stored as JSONB for detailed analysis
121+
all_matches JSONB DEFAULT '[]'::jsonb,
122+
123+
-- Performance metrics
124+
processing_time_ms INTEGER,
125+
model_version VARCHAR(50),
126+
127+
-- Security & Audit
128+
requested_by INTEGER, -- User ID
129+
user_role VARCHAR(50),
130+
ip_address INET,
131+
user_agent TEXT,
132+
133+
-- Timestamps
134+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
135+
136+
-- Investigation reference
137+
case_id VARCHAR(100),
138+
investigation_notes TEXT
139+
);
140+
141+
-- ====================================================================
142+
-- INDEXES FOR PERFORMANCE
143+
-- ====================================================================
144+
145+
-- Criminal table indexes
146+
CREATE INDEX idx_criminal_nic ON criminals(nic);
147+
CREATE INDEX idx_criminal_name ON criminals USING gin(to_tsvector('english', name));
148+
CREATE INDEX idx_criminal_status ON criminals(status) WHERE status = 'active';
149+
CREATE INDEX idx_criminal_risk_level ON criminals(risk_level);
150+
CREATE INDEX idx_criminal_created_at ON criminals(created_at DESC);
151+
152+
-- Suspect photos indexes
153+
CREATE INDEX idx_suspect_photos_criminal_id ON suspect_photos(criminal_id);
154+
CREATE INDEX idx_suspect_photos_primary ON suspect_photos(criminal_id, is_primary) WHERE is_primary = true;
155+
CREATE INDEX idx_suspect_photos_hash ON suspect_photos(photo_hash);
156+
157+
-- Facial recognition logs indexes
158+
CREATE INDEX idx_fr_logs_created_at ON facial_recognition_logs(created_at DESC);
159+
CREATE INDEX idx_fr_logs_user ON facial_recognition_logs(requested_by);
160+
CREATE INDEX idx_fr_logs_best_match ON facial_recognition_logs(best_match_criminal_id) WHERE best_match_criminal_id IS NOT NULL;
161+
CREATE INDEX idx_fr_logs_case_id ON facial_recognition_logs(case_id) WHERE case_id IS NOT NULL;
162+
163+
-- ====================================================================
164+
-- TRIGGERS FOR AUTO-UPDATE
165+
-- ====================================================================
166+
167+
-- Automatically update updated_at timestamp
168+
CREATE OR REPLACE FUNCTION update_updated_at_column()
169+
RETURNS TRIGGER AS $$
170+
BEGIN
171+
NEW.updated_at = CURRENT_TIMESTAMP;
172+
RETURN NEW;
173+
END;
174+
$$ LANGUAGE plpgsql;
175+
176+
CREATE TRIGGER update_criminals_updated_at
177+
BEFORE UPDATE ON criminals
178+
FOR EACH ROW
179+
EXECUTE FUNCTION update_updated_at_column();
180+
181+
-- ====================================================================
182+
-- UTILITY FUNCTIONS
183+
-- ====================================================================
184+
185+
-- Function to calculate average embedding from multiple photos
186+
CREATE OR REPLACE FUNCTION calculate_average_embedding(p_criminal_id INTEGER)
187+
RETURNS BYTEA AS $$
188+
DECLARE
189+
avg_embedding BYTEA;
190+
BEGIN
191+
-- This will be called from Python after uploading multiple photos
192+
-- Python will handle the actual embedding averaging logic
193+
-- This function is a placeholder for future stored procedure implementation
194+
RETURN NULL;
195+
END;
196+
$$ LANGUAGE plpgsql;
197+
198+
-- Function to search similar faces (placeholder - actual search done in Python)
199+
CREATE OR REPLACE FUNCTION search_similar_faces(
200+
p_embedding BYTEA,
201+
p_threshold DECIMAL DEFAULT 0.75,
202+
p_limit INTEGER DEFAULT 10
203+
)
204+
RETURNS TABLE (
205+
criminal_id INTEGER,
206+
name VARCHAR,
207+
similarity DECIMAL
208+
) AS $$
209+
BEGIN
210+
-- Actual similarity search is performed in Python using numpy
211+
-- This is a placeholder for documentation
212+
RETURN QUERY SELECT NULL::INTEGER, NULL::VARCHAR, NULL::DECIMAL LIMIT 0;
213+
END;
214+
$$ LANGUAGE plpgsql;
215+
216+
-- ====================================================================
217+
-- INITIAL DATA / SEED DATA
218+
-- ====================================================================
219+
220+
-- Insert sample criminal record for testing
221+
INSERT INTO criminals (
222+
name,
223+
nic,
224+
date_of_birth,
225+
gender,
226+
crime_history,
227+
status,
228+
risk_level,
229+
notes
230+
) VALUES (
231+
'Test Suspect One',
232+
'199012345678',
233+
'1990-05-15',
234+
'Male',
235+
'[{"crime_type": "Theft", "date": "2023-03-10", "status": "Convicted", "sentence": "2 years"}]'::jsonb,
236+
'active',
237+
'medium',
238+
'Sample criminal record for testing facial recognition system'
239+
);
240+
241+
-- ====================================================================
242+
-- PERMISSIONS & SECURITY
243+
-- ====================================================================
244+
245+
-- Grant appropriate permissions (adjust based on your user roles)
246+
-- GRANT SELECT, INSERT, UPDATE ON criminals TO crimelink_app_user;
247+
-- GRANT SELECT, INSERT ON suspect_photos TO crimelink_app_user;
248+
-- GRANT INSERT ON facial_recognition_logs TO crimelink_app_user;
249+
250+
-- ====================================================================
251+
-- COMMENTS FOR DOCUMENTATION
252+
-- ====================================================================
253+
254+
COMMENT ON TABLE criminals IS 'Stores criminal records with biometric face embeddings for facial recognition';
255+
COMMENT ON COLUMN criminals.face_embedding IS 'Average face embedding vector stored as binary data (512-dimensional float32 array)';
256+
COMMENT ON COLUMN criminals.crime_history IS 'JSON array of crime records: [{crime_type, date, status, sentence}]';
257+
258+
COMMENT ON TABLE suspect_photos IS 'Multiple photos per criminal for improved recognition accuracy';
259+
COMMENT ON COLUMN suspect_photos.face_embedding IS 'Individual face embedding for this specific photo';
260+
COMMENT ON COLUMN suspect_photos.photo_hash IS 'SHA-256 hash to prevent duplicate photo uploads';
261+
262+
COMMENT ON TABLE facial_recognition_logs IS 'Audit trail for all facial recognition analysis requests';
263+
COMMENT ON COLUMN facial_recognition_logs.all_matches IS 'JSON array of all matches: [{criminal_id, similarity, confidence}]';
264+
265+
-- ====================================================================
266+
-- END OF SCHEMA
267+
-- ====================================================================
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.crimeLink.analyzer.config;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.http.client.SimpleClientHttpRequestFactory;
6+
import org.springframework.web.client.RestTemplate;
7+
8+
/**
9+
* Configuration for RestTemplate bean used to communicate with ML microservices.
10+
* Part of the hybrid monolith + microservices architecture.
11+
*/
12+
@Configuration
13+
public class RestTemplateConfig {
14+
15+
@Bean
16+
public RestTemplate restTemplate() {
17+
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
18+
// Set timeout for ML service calls (30 seconds for heavy processing)
19+
factory.setConnectTimeout(10000); // 10 seconds connection timeout
20+
factory.setReadTimeout(30000); // 30 seconds read timeout (ML processing can be slow)
21+
return new RestTemplate(factory);
22+
}
23+
}

src/main/java/com/crimeLink/analyzer/config/SecurityConfig.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
4646
.requestMatchers("/api/auth/**").permitAll()
4747
.requestMatchers("/api/health").permitAll()
4848
.requestMatchers("/api/admin/health").permitAll()
49+
.requestMatchers("/api/facial/health").permitAll() // ML service health check
50+
.requestMatchers("/api/call-analysis/health").permitAll() // ML service health check
51+
52+
// ML Service endpoints - Investigator role only
53+
.requestMatchers("/api/call-analysis/**").hasRole("Investigator")
54+
.requestMatchers("/api/facial/**").hasRole("Investigator")
55+
4956
.requestMatchers("/api/database/**").permitAll()
5057
.requestMatchers("/api/test").permitAll()
5158
.requestMatchers("/api/debug/**").permitAll() // 🔍 Debug endpoints

0 commit comments

Comments
 (0)