fix: add sanitizePostgresText to filter null bytes and malformed Unicode escape sequences before inserting template text into Supabase
This commit is contained in:
@@ -397,6 +397,20 @@ export const ocrImage = async (req: AuthenticatedRequest, res: Response) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PostgreSQL ve Supabase JSON/Text alanlarının reddettiği null byte (\u0000),
|
||||||
|
* bozuk unicode kaçış dizileri ve kontrol karakterlerini temizler.
|
||||||
|
*/
|
||||||
|
export function sanitizePostgresText(text: string | null | undefined): string {
|
||||||
|
if (!text) return '';
|
||||||
|
return String(text)
|
||||||
|
.replace(/\0/g, '')
|
||||||
|
.replace(/\\u0000/gi, '')
|
||||||
|
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, '')
|
||||||
|
.replace(/[\uD800-\uDFFF]/g, '')
|
||||||
|
.replace(/\uFFFD/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
// uploadDocument ve uploadTemplate'in (template.controller.ts) ortak kullandığı
|
// uploadDocument ve uploadTemplate'in (template.controller.ts) ortak kullandığı
|
||||||
// metin çıkarma adımı: resimler için Tesseract OCR, ofis belgeleri için officeParser.
|
// metin çıkarma adımı: resimler için Tesseract OCR, ofis belgeleri için officeParser.
|
||||||
export async function extractTextFromUploadedFile(file: { buffer: Buffer; mimetype: string; originalname: string }): Promise<string> {
|
export async function extractTextFromUploadedFile(file: { buffer: Buffer; mimetype: string; originalname: string }): Promise<string> {
|
||||||
@@ -444,7 +458,7 @@ export async function extractTextFromUploadedFile(file: { buffer: Buffer; mimety
|
|||||||
.replace(/\s+/g, " ")
|
.replace(/\s+/g, " ")
|
||||||
.trim();
|
.trim();
|
||||||
if (plainText.length > 20) {
|
if (plainText.length > 20) {
|
||||||
return plainText;
|
return sanitizePostgresText(plainText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,7 +469,7 @@ export async function extractTextFromUploadedFile(file: { buffer: Buffer; mimety
|
|||||||
const pdfResult: any = await officeParser.parseOffice(pdfBuffer);
|
const pdfResult: any = await officeParser.parseOffice(pdfBuffer);
|
||||||
const pdfText = typeof pdfResult === 'string' ? pdfResult : (pdfResult?.toText ? pdfResult.toText() : String(pdfResult || ''));
|
const pdfText = typeof pdfResult === 'string' ? pdfResult : (pdfResult?.toText ? pdfResult.toText() : String(pdfResult || ''));
|
||||||
if (pdfText && pdfText.trim()) {
|
if (pdfText && pdfText.trim()) {
|
||||||
return pdfText.trim();
|
return sanitizePostgresText(pdfText.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -464,8 +478,9 @@ export async function extractTextFromUploadedFile(file: { buffer: Buffer; mimety
|
|||||||
const rawText = file.buffer.toString('utf8');
|
const rawText = file.buffer.toString('utf8');
|
||||||
if (rawText && rawText.trim().length > 20) {
|
if (rawText && rawText.trim().length > 20) {
|
||||||
const plainText = rawText.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
const plainText = rawText.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
||||||
if (plainText.length > 20) {
|
const cleaned = sanitizePostgresText(plainText);
|
||||||
return plainText;
|
if (cleaned.length > 20) {
|
||||||
|
return cleaned;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,14 +497,15 @@ export async function extractTextFromUploadedFile(file: { buffer: Buffer; mimety
|
|||||||
imageBuffer = await heicConvert({ buffer: file.buffer, format: 'JPEG', quality: 1 });
|
imageBuffer = await heicConvert({ buffer: file.buffer, format: 'JPEG', quality: 1 });
|
||||||
}
|
}
|
||||||
const { data: { text } } = await Tesseract.recognize(imageBuffer, 'tur');
|
const { data: { text } } = await Tesseract.recognize(imageBuffer, 'tur');
|
||||||
return text;
|
return sanitizePostgresText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// officeParser natively supports pdf, docx, doc, rtf, txt, etc.
|
// officeParser natively supports pdf, docx, doc, rtf, txt, etc.
|
||||||
// officeparser@7.x parseOffice() bir sonuc objesi donduruyor (duz string degil),
|
// officeparser@7.x parseOffice() bir sonuc objesi donduruyor (duz string degil),
|
||||||
// duz metin icin .toText() cagirmak gerekiyor.
|
// duz metin icin .toText() cagirmak gerekiyor.
|
||||||
const result: any = await officeParser.parseOffice(file.buffer);
|
const result: any = await officeParser.parseOffice(file.buffer);
|
||||||
return typeof result === 'string' ? result : (result?.toText ? result.toText() : String(result || ''));
|
const rawResult = typeof result === 'string' ? result : (result?.toText ? result.toText() : String(result || ''));
|
||||||
|
return sanitizePostgresText(rawResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sohbet ekranlarındaki (genel Sohbet ve dosya bazlı "AyrisLegal'e Sor") ataç
|
// Sohbet ekranlarındaki (genel Sohbet ve dosya bazlı "AyrisLegal'e Sor") ataç
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { supabase } from '../lib/supabase';
|
import { supabase } from '../lib/supabase';
|
||||||
import { AuthenticatedRequest } from '../middleware/auth';
|
import { AuthenticatedRequest } from '../middleware/auth';
|
||||||
import { extractTextFromUploadedFile } from './document.controller';
|
import { extractTextFromUploadedFile, sanitizePostgresText } from './document.controller';
|
||||||
|
|
||||||
// Belge Şablonları: avukatın büro genelinde kullandığı dilekçe/evrak şablonları.
|
// Belge Şablonları: avukatın büro genelinde kullandığı dilekçe/evrak şablonları.
|
||||||
// Ayrı bir Storage bucket açmaya gerek kalmasın diye documents'ın kullandığı
|
// Ayrı bir Storage bucket açmaya gerek kalmasın diye documents'ın kullandığı
|
||||||
@@ -45,17 +45,22 @@ export const uploadTemplate = async (req: AuthenticatedRequest, res: Response) =
|
|||||||
return res.status(500).json({ error: 'Şablon dosyası yüklenemedi.' });
|
return res.status(500).json({ error: 'Şablon dosyası yüklenemedi.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cleanName = sanitizePostgresText(String(name)).slice(0, 200);
|
||||||
|
const cleanCategory = category ? sanitizePostgresText(String(category)) : null;
|
||||||
|
const cleanDescription = description ? sanitizePostgresText(String(description)) : null;
|
||||||
|
const cleanText = extractedText ? sanitizePostgresText(extractedText) : null;
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('templates')
|
.from('templates')
|
||||||
.insert([{
|
.insert([{
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
category: category || null,
|
category: cleanCategory,
|
||||||
name: String(name).slice(0, 200),
|
name: cleanName,
|
||||||
description: description || null,
|
description: cleanDescription,
|
||||||
storage_path: storagePath,
|
storage_path: storagePath,
|
||||||
file_size: file.size,
|
file_size: file.size,
|
||||||
mime_type: file.mimetype,
|
mime_type: file.mimetype,
|
||||||
extracted_text: extractedText,
|
extracted_text: cleanText,
|
||||||
}])
|
}])
|
||||||
.select()
|
.select()
|
||||||
.single();
|
.single();
|
||||||
|
|||||||
Reference in New Issue
Block a user