diff --git a/frontend/src/pages/SettingsPage.jsx b/frontend/src/pages/SettingsPage.jsx
index 2f13757..fd1f291 100644
--- a/frontend/src/pages/SettingsPage.jsx
+++ b/frontend/src/pages/SettingsPage.jsx
@@ -7,6 +7,7 @@ import {
Tabs,
Tab,
FormControl,
+ FormControlLabel,
InputLabel,
Select,
MenuItem,
@@ -29,6 +30,7 @@ import {
Card,
CardContent,
Divider,
+ Switch,
} from '@mui/material';
import { Edit as EditIcon, Delete as DeleteIcon, Add as AddIcon } from '@mui/icons-material';
import { useAuth } from '../contexts/AuthContext';
@@ -39,6 +41,21 @@ const SettingsPage = () => {
const [tabValue, setTabValue] = useState(0);
const [settings, setSettings] = useState({
ocrLanguage: 'eng',
+ concurrentOcrJobs: 4,
+ ocrTimeoutSeconds: 300,
+ maxFileSizeMb: 50,
+ allowedFileTypes: ['pdf', 'png', 'jpg', 'jpeg', 'tiff', 'bmp', 'txt'],
+ autoRotateImages: true,
+ enableImagePreprocessing: true,
+ searchResultsPerPage: 25,
+ searchSnippetLength: 200,
+ fuzzySearchThreshold: 0.8,
+ retentionDays: null,
+ enableAutoCleanup: false,
+ enableCompression: false,
+ memoryLimitMb: 512,
+ cpuPriority: 'normal',
+ enableBackgroundOcr: true,
});
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
@@ -72,7 +89,24 @@ const SettingsPage = () => {
const fetchSettings = async () => {
try {
const response = await api.get('/settings');
- setSettings(response.data);
+ setSettings({
+ ocrLanguage: response.data.ocr_language || 'eng',
+ concurrentOcrJobs: response.data.concurrent_ocr_jobs || 4,
+ ocrTimeoutSeconds: response.data.ocr_timeout_seconds || 300,
+ maxFileSizeMb: response.data.max_file_size_mb || 50,
+ allowedFileTypes: response.data.allowed_file_types || ['pdf', 'png', 'jpg', 'jpeg', 'tiff', 'bmp', 'txt'],
+ autoRotateImages: response.data.auto_rotate_images !== undefined ? response.data.auto_rotate_images : true,
+ enableImagePreprocessing: response.data.enable_image_preprocessing !== undefined ? response.data.enable_image_preprocessing : true,
+ searchResultsPerPage: response.data.search_results_per_page || 25,
+ searchSnippetLength: response.data.search_snippet_length || 200,
+ fuzzySearchThreshold: response.data.fuzzy_search_threshold || 0.8,
+ retentionDays: response.data.retention_days,
+ enableAutoCleanup: response.data.enable_auto_cleanup || false,
+ enableCompression: response.data.enable_compression || false,
+ memoryLimitMb: response.data.memory_limit_mb || 512,
+ cpuPriority: response.data.cpu_priority || 'normal',
+ enableBackgroundOcr: response.data.enable_background_ocr !== undefined ? response.data.enable_background_ocr : true,
+ });
} catch (error) {
console.error('Error fetching settings:', error);
if (error.response?.status !== 404) {
@@ -96,7 +130,14 @@ const SettingsPage = () => {
const handleSettingsChange = async (key, value) => {
setLoading(true);
try {
- await api.put('/settings', { ...settings, [key]: value });
+ // Convert camelCase to snake_case for API
+ const snakeCase = (str) => str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
+ const apiKey = snakeCase(key);
+
+ // Build the update payload with only the changed field
+ const updatePayload = { [apiKey]: value };
+
+ await api.put('/settings', updatePayload);
setSettings({ ...settings, [key]: value });
showSnackbar('Settings updated successfully', 'success');
} catch (error) {
@@ -199,24 +240,258 @@ const SettingsPage = () => {
OCR Configuration
-
- OCR Language
-
-
-
- Select the primary language for OCR text extraction. This affects how accurately text is recognized from images and scanned documents.
+
+
+
+ OCR Language
+
+
+
+
+ handleSettingsChange('concurrentOcrJobs', parseInt(e.target.value))}
+ disabled={loading}
+ inputProps={{ min: 1, max: 16 }}
+ helperText="Number of OCR jobs that can run simultaneously"
+ />
+
+
+ handleSettingsChange('ocrTimeoutSeconds', parseInt(e.target.value))}
+ disabled={loading}
+ inputProps={{ min: 30, max: 3600 }}
+ helperText="Maximum time for OCR processing per file"
+ />
+
+
+
+ CPU Priority
+
+
+
+
+
+
+
+
+
+
+ File Processing
+
+
+
+ handleSettingsChange('maxFileSizeMb', parseInt(e.target.value))}
+ disabled={loading}
+ inputProps={{ min: 1, max: 500 }}
+ helperText="Maximum allowed file size for uploads"
+ />
+
+
+ handleSettingsChange('memoryLimitMb', parseInt(e.target.value))}
+ disabled={loading}
+ inputProps={{ min: 128, max: 4096 }}
+ helperText="Memory limit per OCR job"
+ />
+
+
+
+ handleSettingsChange('autoRotateImages', e.target.checked)}
+ disabled={loading}
+ />
+ }
+ label="Auto-rotate Images"
+ />
+
+ Automatically detect and correct image orientation
+
+
+
+
+
+ handleSettingsChange('enableImagePreprocessing', e.target.checked)}
+ disabled={loading}
+ />
+ }
+ label="Enable Image Preprocessing"
+ />
+
+ Enhance images for better OCR accuracy (deskew, denoise, contrast)
+
+
+
+
+
+ handleSettingsChange('enableBackgroundOcr', e.target.checked)}
+ disabled={loading}
+ />
+ }
+ label="Enable Background OCR"
+ />
+
+ Process OCR in the background after file upload
+
+
+
+
+
+
+
+
+
+
+ Search Configuration
+
+
+
+
+
+ Results Per Page
+
+
+
+
+ handleSettingsChange('searchSnippetLength', parseInt(e.target.value))}
+ disabled={loading}
+ inputProps={{ min: 50, max: 500 }}
+ helperText="Characters to show in search result previews"
+ />
+
+
+ handleSettingsChange('fuzzySearchThreshold', parseFloat(e.target.value))}
+ disabled={loading}
+ inputProps={{ min: 0, max: 1, step: 0.1 }}
+ helperText="Tolerance for spelling mistakes (0.0-1.0)"
+ />
+
+
+
+
+
+
+
+
+ Storage Management
+
+
+
+
+ handleSettingsChange('retentionDays', e.target.value ? parseInt(e.target.value) : null)}
+ disabled={loading}
+ inputProps={{ min: 1 }}
+ helperText="Auto-delete documents after X days (leave empty to disable)"
+ />
+
+
+
+ handleSettingsChange('enableAutoCleanup', e.target.checked)}
+ disabled={loading}
+ />
+ }
+ label="Enable Auto Cleanup"
+ />
+
+ Automatically remove orphaned files and clean up storage
+
+
+
+
+
+ handleSettingsChange('enableCompression', e.target.checked)}
+ disabled={loading}
+ />
+ }
+ label="Enable Compression"
+ />
+
+ Compress stored documents to save disk space
+
+
+
+
diff --git a/src/db.rs b/src/db.rs
index 1aab516..6242022 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -92,6 +92,21 @@ impl Database {
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE,
ocr_language VARCHAR(10) DEFAULT 'eng',
+ concurrent_ocr_jobs INT DEFAULT 4,
+ ocr_timeout_seconds INT DEFAULT 300,
+ max_file_size_mb INT DEFAULT 50,
+ allowed_file_types TEXT[] DEFAULT ARRAY['pdf', 'png', 'jpg', 'jpeg', 'tiff', 'bmp', 'txt'],
+ auto_rotate_images BOOLEAN DEFAULT TRUE,
+ enable_image_preprocessing BOOLEAN DEFAULT TRUE,
+ search_results_per_page INT DEFAULT 25,
+ search_snippet_length INT DEFAULT 200,
+ fuzzy_search_threshold REAL DEFAULT 0.8,
+ retention_days INT,
+ enable_auto_cleanup BOOLEAN DEFAULT FALSE,
+ enable_compression BOOLEAN DEFAULT FALSE,
+ memory_limit_mb INT DEFAULT 512,
+ cpu_priority VARCHAR(10) DEFAULT 'normal',
+ enable_background_ocr BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)
@@ -569,7 +584,12 @@ impl Database {
pub async fn get_user_settings(&self, user_id: Uuid) -> Result