Compare commits
2 Commits
add-llm-op
...
5fa6b921d8
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fa6b921d8 | |||
|
|
feb756cfe2 |
@@ -5,6 +5,11 @@ COPY package.json package-lock.json* ./
|
|||||||
COPY shared ./shared
|
COPY shared ./shared
|
||||||
COPY backend ./backend
|
COPY backend ./backend
|
||||||
|
|
||||||
|
# Увеличенные таймауты для нестабильной сети
|
||||||
|
RUN npm config set fetch-retry-mintimeout 20000 && \
|
||||||
|
npm config set fetch-retry-maxtimeout 120000 && \
|
||||||
|
npm config set fetch-timeout 300000
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
FROM node:20-alpine AS build
|
FROM node:20-alpine AS build
|
||||||
@@ -22,6 +27,10 @@ FROM node:20-alpine AS runner
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
# @napi-rs/canvas (dep of pdf-parse) ships a musl pre-built binary that
|
||||||
|
# needs these compatibility / font libraries at runtime.
|
||||||
|
RUN apk add --no-cache libc6-compat fontconfig freetype
|
||||||
|
|
||||||
COPY --from=build /app/backend/dist ./dist
|
COPY --from=build /app/backend/dist ./dist
|
||||||
COPY --from=build /app/backend/package.json ./package.json
|
COPY --from=build /app/backend/package.json ./package.json
|
||||||
COPY --from=build /app/node_modules ./node_modules
|
COPY --from=build /app/node_modules ./node_modules
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ COPY package.json package-lock.json* tsconfig.json* ./
|
|||||||
COPY shared ./shared
|
COPY shared ./shared
|
||||||
COPY frontend ./frontend
|
COPY frontend ./frontend
|
||||||
|
|
||||||
|
# Увеличиваем таймауты и повторы для нестабильной сети
|
||||||
|
RUN npm config set fetch-retry-mintimeout 20000 && \
|
||||||
|
npm config set fetch-retry-maxtimeout 120000 && \
|
||||||
|
npm config set fetch-timeout 300000
|
||||||
|
|
||||||
# Устанавливаем зависимости из корня
|
# Устанавливаем зависимости из корня
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ export const pool = new Pool({
|
|||||||
database: config.db.database,
|
database: config.db.database,
|
||||||
user: config.db.user,
|
user: config.db.user,
|
||||||
password: config.db.password,
|
password: config.db.password,
|
||||||
|
connectionTimeoutMillis: 5000,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { PDFParse } from 'pdf-parse';
|
|
||||||
import OpenAI from 'openai';
|
import OpenAI from 'openai';
|
||||||
import { config } from '../config';
|
import { config } from '../config';
|
||||||
import type { StatementFile } from '@family-budget/shared';
|
import type { StatementFile } from '@family-budget/shared';
|
||||||
@@ -64,6 +63,18 @@ export function isPdfConversionError(r: unknown): r is PdfConversionError {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lazy-loaded to avoid crashing the app at startup — pdf-parse pulls in
|
||||||
|
// @napi-rs/canvas (native Skia binary) which may fail on Alpine.
|
||||||
|
let _PDFParse: typeof import('pdf-parse')['PDFParse'] | undefined;
|
||||||
|
function loadPDFParse() {
|
||||||
|
if (!_PDFParse) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
|
const mod = require('pdf-parse') as typeof import('pdf-parse');
|
||||||
|
_PDFParse = mod.PDFParse;
|
||||||
|
}
|
||||||
|
return _PDFParse;
|
||||||
|
}
|
||||||
|
|
||||||
export async function convertPdfToStatement(
|
export async function convertPdfToStatement(
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
): Promise<StatementFile | PdfConversionError> {
|
): Promise<StatementFile | PdfConversionError> {
|
||||||
@@ -77,6 +88,7 @@ export async function convertPdfToStatement(
|
|||||||
|
|
||||||
let text: string;
|
let text: string;
|
||||||
try {
|
try {
|
||||||
|
const PDFParse = loadPDFParse();
|
||||||
const parser = new PDFParse({ data: buffer });
|
const parser = new PDFParse({ data: buffer });
|
||||||
const result = await parser.getText();
|
const result = await parser.getText();
|
||||||
text = result.text || '';
|
text = result.text || '';
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export function ImportModal({ onClose, onDone }: Props) {
|
|||||||
|
|
||||||
{result && (
|
{result && (
|
||||||
<div className="import-result">
|
<div className="import-result">
|
||||||
<div className="import-result-icon">✓</div>
|
<div className="import-result-icon" aria-hidden="true">✓</div>
|
||||||
<h3>Импорт завершён</h3>
|
<h3>Импорт завершён</h3>
|
||||||
<table className="import-stats">
|
<table className="import-stats">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
Reference in New Issue
Block a user