This commit addresses multiple critical fixes and adds new functionality for the NaviDocs local testing environment (port 8083): Search Fixes: - Fixed search to use backend /api/search instead of direct Meilisearch - Resolves network accessibility issue when accessing from external IPs - Search now works from http://172.29.75.55:8083/search PDF Text Selection: - Added PDF.js text layer for selectable text - Imported pdf_viewer.css for proper text layer styling - Changed text layer opacity to 1 for better interaction - Added user-select: text for improved text selection - Pink selection highlight (rgba(255, 92, 178, 0.3)) Database Cleanup: - Created cleanup scripts to remove 20 duplicate documents - Removed 753 orphaned entries from Meilisearch index - Cleaned 17 document folders from filesystem - Kept only newest version of each document - Scripts: clean-duplicates.js, clean-meilisearch-orphans.js Auto-Fill Feature: - New /api/upload/quick-ocr endpoint for first-page OCR - Automatically extracts metadata from PDFs on file selection - Detects: boat make, model, year, name, and document title - Checks both OCR text and filename for boat name - Auto-fills upload form with extracted data - Shows loading indicator during metadata extraction - Graceful fallback to filename if OCR fails Tenant Management: - Updated organization ID to use boat name as tenant - Falls back to "Liliane 1" for single-tenant setup - Each boat becomes a unique tenant in the system Files Changed: - client/src/views/DocumentView.vue - Text layer implementation - client/src/composables/useSearch.js - Backend API integration - client/src/components/UploadModal.vue - Auto-fill feature - server/routes/quick-ocr.js - OCR endpoint (new) - server/index.js - Route registration - server/scripts/* - Cleanup utilities (new) Testing: All features tested on local deployment at http://172.29.75.55:8083 - Backend: http://localhost:8001 - Frontend: http://localhost:8083 - Meilisearch: http://localhost:7700 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
113 lines
2.9 KiB
JavaScript
113 lines
2.9 KiB
JavaScript
/**
|
|
* NaviDocs Backend API
|
|
* Express server with SQLite + Meilisearch
|
|
*/
|
|
|
|
import express from 'express';
|
|
import helmet from 'helmet';
|
|
import cors from 'cors';
|
|
import rateLimit from 'express-rate-limit';
|
|
import dotenv from 'dotenv';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, join } from 'path';
|
|
|
|
// Load environment variables
|
|
dotenv.config();
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const PORT = process.env.PORT || 3001;
|
|
const NODE_ENV = process.env.NODE_ENV || 'development';
|
|
|
|
// Create Express app
|
|
const app = express();
|
|
|
|
// Security middleware
|
|
app.use(helmet({
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
scriptSrc: ["'self'", "'unsafe-inline'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
imgSrc: ["'self'", 'data:', 'blob:'],
|
|
connectSrc: ["'self'"],
|
|
fontSrc: ["'self'"],
|
|
objectSrc: ["'none'"],
|
|
mediaSrc: ["'self'"],
|
|
frameSrc: ["'none'"]
|
|
}
|
|
},
|
|
crossOriginEmbedderPolicy: false
|
|
}));
|
|
|
|
// CORS
|
|
app.use(cors({
|
|
origin: NODE_ENV === 'production' ? process.env.ALLOWED_ORIGINS?.split(',') : '*',
|
|
credentials: true
|
|
}));
|
|
|
|
// Body parsing
|
|
app.use(express.json({ limit: '10mb' }));
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
|
|
// Rate limiting
|
|
const limiter = rateLimit({
|
|
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000'), // 15 minutes
|
|
max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || '100'),
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
message: 'Too many requests, please try again later'
|
|
});
|
|
|
|
app.use('/api/', limiter);
|
|
|
|
// Health check
|
|
app.get('/health', async (req, res) => {
|
|
try {
|
|
// TODO: Check database, Meilisearch, queue
|
|
res.json({
|
|
status: 'ok',
|
|
timestamp: Date.now(),
|
|
uptime: process.uptime()
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
status: 'error',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// Import route modules
|
|
import uploadRoutes from './routes/upload.js';
|
|
import quickOcrRoutes from './routes/quick-ocr.js';
|
|
import jobsRoutes from './routes/jobs.js';
|
|
import searchRoutes from './routes/search.js';
|
|
import documentsRoutes from './routes/documents.js';
|
|
import imagesRoutes from './routes/images.js';
|
|
|
|
// API routes
|
|
app.use('/api/upload/quick-ocr', quickOcrRoutes);
|
|
app.use('/api/upload', uploadRoutes);
|
|
app.use('/api/jobs', jobsRoutes);
|
|
app.use('/api/search', searchRoutes);
|
|
app.use('/api/documents', documentsRoutes);
|
|
app.use('/api', imagesRoutes);
|
|
|
|
// Error handling
|
|
app.use((err, req, res, next) => {
|
|
console.error('Error:', err);
|
|
|
|
res.status(err.status || 500).json({
|
|
error: err.message || 'Internal server error',
|
|
...(NODE_ENV === 'development' && { stack: err.stack })
|
|
});
|
|
});
|
|
|
|
// Start server
|
|
app.listen(PORT, () => {
|
|
console.log(`NaviDocs API listening on port ${PORT}`);
|
|
console.log(`Environment: ${NODE_ENV}`);
|
|
console.log(`Health check: http://localhost:${PORT}/health`);
|
|
});
|
|
|
|
export default app;
|