fix mongo

This commit is contained in:
2025-11-04 19:46:39 +03:00
parent 69eddf47db
commit c4664edd7e
7 changed files with 60 additions and 153 deletions

View File

@@ -1,99 +0,0 @@
const mongoose = require('mongoose');
// Get MongoDB URL from environment variables
// MONGO_ADDR is a centralized env variable from server/utils/const.ts
const primaryUri = process.env.MONGO_ADDR || process.env.MONGODB_URI || 'mongodb://localhost:27017/procurement_db';
const fallbackUri = process.env.MONGODB_AUTH_URI || 'mongodb://admin:password@localhost:27017/procurement_db?authSource=admin';
/**
* Check if error is related to authentication
*/
const isAuthError = (error) => {
if (!error) {
return false;
}
const authCodes = new Set([18, 13]);
if (error.code && authCodes.has(error.code)) {
return true;
}
const message = String(error.message || '').toLowerCase();
return message.includes('auth') || message.includes('authentication');
};
/**
* Try to connect to MongoDB with specific URI
*/
const connectWithUri = async (uri, label) => {
console.log(`\n📡 Попытка подключения к MongoDB (${label})...`);
if (process.env.DEV === 'true') {
console.log(` URI: ${uri}`);
}
const connection = await mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverSelectionTimeoutMS: 5000,
connectTimeoutMS: 5000,
});
try {
if (connection?.connection?.db) {
await connection.connection.db.admin().command({ ping: 1 });
}
} catch (pingError) {
if (isAuthError(pingError)) {
await mongoose.connection.close().catch(() => {});
throw pingError;
}
console.error('⚠️ MongoDB ping error:', pingError.message);
}
console.log('✅ MongoDB подключена успешно!');
console.log(` Хост: ${connection?.connection?.host || 'не указан'}`);
console.log(` БД: ${connection?.connection?.name || 'не указана'}\n`);
if (process.env.DEV === 'true') {
console.log(` Пользователь: ${connection?.connection?.user || 'anonymous'}`);
}
return connection;
};
/**
* Connect to MongoDB with fallback strategy
*/
const connectDB = async () => {
const attempts = [];
if (fallbackUri) {
attempts.push({ uri: fallbackUri, label: 'AUTH' });
}
attempts.push({ uri: primaryUri, label: 'PRIMARY' });
let lastError = null;
for (const attempt of attempts) {
try {
console.log(`[MongoDB] Trying ${attempt.label} connection...`);
return await connectWithUri(attempt.uri, attempt.label);
} catch (error) {
lastError = error;
console.error(`\n❌ Ошибка подключения к MongoDB (${attempt.label}):`);
console.error(` ${error.message}\n`);
if (!isAuthError(error)) {
break;
}
}
}
if (lastError) {
console.warn('⚠️ Приложение продолжит работу с mock данными\n');
}
return null;
};
module.exports = connectDB;

View File

@@ -2,7 +2,7 @@ const express = require('express');
const cors = require('cors'); const cors = require('cors');
const dotenv = require('dotenv'); const dotenv = require('dotenv');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const mongoose = require('mongoose');
// Загрузить переменные окружения // Загрузить переменные окружения
dotenv.config(); dotenv.config();
@@ -28,15 +28,10 @@ const buyProductsRoutes = require('./routes/buyProducts');
const requestsRoutes = require('./routes/requests'); const requestsRoutes = require('./routes/requests');
const homeRoutes = require('./routes/home'); const homeRoutes = require('./routes/home');
const connectDB = require('./config/db');
const app = express(); const app = express();
// Подключить MongoDB при инициализации // Проверить подключение к MongoDB (подключение происходит в server/utils/mongoose.ts)
let dbConnected = false; const dbConnected = mongoose.connection.readyState === 1;
connectDB().then(() => {
dbConnected = true;
});
// Middleware // Middleware
app.use(cors()); app.use(cors());
@@ -66,7 +61,7 @@ const delay = (ms = 300) => (req, res, next) => setTimeout(next, ms);
app.use(delay()); app.use(delay());
// Статика для загруженных файлов // Статика для загруженных файлов
const uploadsRoot = path.join(__dirname, '..', '..', 'remote-assets', 'uploads'); const uploadsRoot = 'server/remote-assets/uploads';
if (!fs.existsSync(uploadsRoot)) { if (!fs.existsSync(uploadsRoot)) {
fs.mkdirSync(uploadsRoot, { recursive: true }); fs.mkdirSync(uploadsRoot, { recursive: true });
} }

View File

@@ -9,7 +9,6 @@ const Message = require('../models/Message');
const Review = require('../models/Review'); const Review = require('../models/Review');
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const { Types } = mongoose; const { Types } = mongoose;
const connectDB = require('../config/db');
const PRESET_COMPANY_ID = new Types.ObjectId('68fe2ccda3526c303ca06796'); const PRESET_COMPANY_ID = new Types.ObjectId('68fe2ccda3526c303ca06796');
const PRESET_USER_EMAIL = 'admin@test-company.ru'; const PRESET_USER_EMAIL = 'admin@test-company.ru';
@@ -140,17 +139,15 @@ const waitForDatabaseConnection = async () => {
} }
try { try {
const connection = await connectDB(); // Ожидаем подключения (подключение происходит автоматически через server/utils/mongoose.ts)
if (!connection) { await new Promise(resolve => setTimeout(resolve, 500));
break;
if (mongoose.connection.readyState === 1) {
const authed = await verifyAuth();
if (authed) {
return;
}
} }
const authed = await verifyAuth();
if (authed) {
return;
}
await mongoose.connection.close().catch(() => {});
} catch (error) { } catch (error) {
if (!isAuthFailure(error)) { if (!isAuthFailure(error)) {
throw error; throw error;
@@ -221,12 +218,8 @@ const initializeTestUser = async () => {
} catch (error) { } catch (error) {
console.error('Error initializing test data:', error.message); console.error('Error initializing test data:', error.message);
if (error?.code === 13 || /auth/i.test(error?.message || '')) { if (error?.code === 13 || /auth/i.test(error?.message || '')) {
try { if (process.env.DEV === 'true') {
await connectDB(); console.error('Auth error detected. Connection managed by server/utils/mongoose.ts');
} catch (connectError) {
if (process.env.DEV === 'true') {
console.error('Failed to re-connect after auth error:', connectError.message);
}
} }
} }
} }

View File

@@ -1,11 +1,10 @@
const express = require('express') const express = require('express')
const fs = require('fs') const fs = require('fs')
const path = require('path')
const router = express.Router() const router = express.Router()
const BuyDocument = require('../models/BuyDocument') const BuyDocument = require('../models/BuyDocument')
// Create remote-assets/docs directory if it doesn't exist // Create remote-assets/docs directory if it doesn't exist
const docsDir = path.join(__dirname, '../../remote-assets/docs') const docsDir = 'server/remote-assets/docs'
if (!fs.existsSync(docsDir)) { if (!fs.existsSync(docsDir)) {
fs.mkdirSync(docsDir, { recursive: true }) fs.mkdirSync(docsDir, { recursive: true })
} }
@@ -57,7 +56,7 @@ router.post('/docs', async (req, res) => {
// Save file to disk // Save file to disk
const binaryData = Buffer.from(fileData, 'base64') const binaryData = Buffer.from(fileData, 'base64')
const filePath = path.join(docsDir, `${id}.${type}`) const filePath = `${docsDir}/${id}.${type}`
fs.writeFileSync(filePath, binaryData) fs.writeFileSync(filePath, binaryData)
console.log(`[BUY API] File saved to ${filePath}, size: ${binaryData.length} bytes`) console.log(`[BUY API] File saved to ${filePath}, size: ${binaryData.length} bytes`)
@@ -187,7 +186,7 @@ router.get('/docs/:id/file', async (req, res) => {
return res.status(404).json({ error: 'Document not found' }) return res.status(404).json({ error: 'Document not found' })
} }
const filePath = path.join(docsDir, `${id}.${doc.type}`) const filePath = `${docsDir}/${id}.${doc.type}`
if (!fs.existsSync(filePath)) { if (!fs.existsSync(filePath)) {
console.log('[BUY API] File not found on disk:', filePath) console.log('[BUY API] File not found on disk:', filePath)
return res.status(404).json({ error: 'File not found on disk' }) return res.status(404).json({ error: 'File not found on disk' })

View File

@@ -2,10 +2,9 @@ const express = require('express');
const router = express.Router(); const router = express.Router();
const { verifyToken } = require('../middleware/auth'); const { verifyToken } = require('../middleware/auth');
const BuyProduct = require('../models/BuyProduct'); const BuyProduct = require('../models/BuyProduct');
const path = require('path');
const fs = require('fs'); const fs = require('fs');
const multer = require('multer'); const multer = require('multer');
const UPLOADS_ROOT = path.join(__dirname, '..', '..', 'remote-assets', 'uploads', 'buy-products'); const UPLOADS_ROOT = 'server/remote-assets/uploads/buy-products';
const ensureDirectory = (dirPath) => { const ensureDirectory = (dirPath) => {
if (!fs.existsSync(dirPath)) { if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true }); fs.mkdirSync(dirPath, { recursive: true });
@@ -24,17 +23,28 @@ const ALLOWED_MIME_TYPES = new Set([
'text/csv', 'text/csv',
]); ]);
const getExtension = (filename) => {
const lastDot = filename.lastIndexOf('.');
return lastDot > 0 ? filename.slice(lastDot) : '';
};
const getBasename = (filename) => {
const lastDot = filename.lastIndexOf('.');
const name = lastDot > 0 ? filename.slice(0, lastDot) : filename;
const lastSlash = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\'));
return lastSlash >= 0 ? name.slice(lastSlash + 1) : name;
};
const storage = multer.diskStorage({ const storage = multer.diskStorage({
destination: (req, file, cb) => { destination: (req, file, cb) => {
const productId = req.params.id || 'common'; const productId = req.params.id || 'common';
const productDir = path.join(UPLOADS_ROOT, productId); const productDir = `${UPLOADS_ROOT}/${productId}`;
ensureDirectory(productDir); ensureDirectory(productDir);
cb(null, productDir); cb(null, productDir);
}, },
filename: (req, file, cb) => { filename: (req, file, cb) => {
const originalExtension = path.extname(file.originalname) || ''; const originalExtension = getExtension(file.originalname);
const baseName = path const baseName = getBasename(file.originalname)
.basename(file.originalname, originalExtension)
.replace(/[^a-zA-Z0-9-_]+/g, '_') .replace(/[^a-zA-Z0-9-_]+/g, '_')
.toLowerCase(); .toLowerCase();
cb(null, `${Date.now()}_${baseName}${originalExtension}`); cb(null, `${Date.now()}_${baseName}${originalExtension}`);
@@ -243,7 +253,7 @@ router.post('/:id/files', verifyToken, handleSingleFileUpload, async (req, res)
return res.status(400).json({ error: 'File is required' }); return res.status(400).json({ error: 'File is required' });
} }
const relativePath = path.join('buy-products', id, req.file.filename).replace(/\\/g, '/'); const relativePath = `buy-products/${id}/${req.file.filename}`;
const file = { const file = {
id: `file-${Date.now()}`, id: `file-${Date.now()}`,
name: req.file.originalname, name: req.file.originalname,
@@ -293,7 +303,7 @@ router.delete('/:id/files/:fileId', verifyToken, async (req, res) => {
await product.save(); await product.save();
const storedPath = fileToRemove.storagePath || fileToRemove.url.replace(/^\/uploads\//, ''); const storedPath = fileToRemove.storagePath || fileToRemove.url.replace(/^\/uploads\//, '');
const absolutePath = path.join(__dirname, '..', '..', 'remote-assets', 'uploads', storedPath); const absolutePath = `server/remote-assets/uploads/${storedPath}`;
fs.promises.unlink(absolutePath).catch((unlinkError) => { fs.promises.unlink(absolutePath).catch((unlinkError) => {
if (unlinkError && unlinkError.code !== 'ENOENT') { if (unlinkError && unlinkError.code !== 'ENOENT') {

View File

@@ -3,7 +3,6 @@ const router = express.Router();
const { verifyToken } = require('../middleware/auth'); const { verifyToken } = require('../middleware/auth');
const Request = require('../models/Request'); const Request = require('../models/Request');
const BuyProduct = require('../models/BuyProduct'); const BuyProduct = require('../models/BuyProduct');
const path = require('path');
const fs = require('fs'); const fs = require('fs');
const multer = require('multer'); const multer = require('multer');
@@ -18,7 +17,7 @@ const log = (message, data = '') => {
} }
}; };
const REQUESTS_UPLOAD_ROOT = path.join(__dirname, '..', '..', 'remote-assets', 'uploads', 'requests'); const REQUESTS_UPLOAD_ROOT = 'server/remote-assets/uploads/requests';
const ensureDirectory = (dirPath) => { const ensureDirectory = (dirPath) => {
if (!fs.existsSync(dirPath)) { if (!fs.existsSync(dirPath)) {
@@ -38,17 +37,28 @@ const ALLOWED_REQUEST_MIME_TYPES = new Set([
'text/csv', 'text/csv',
]); ]);
const getExtension = (filename) => {
const lastDot = filename.lastIndexOf('.');
return lastDot > 0 ? filename.slice(lastDot) : '';
};
const getBasename = (filename) => {
const lastDot = filename.lastIndexOf('.');
const name = lastDot > 0 ? filename.slice(0, lastDot) : filename;
const lastSlash = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\'));
return lastSlash >= 0 ? name.slice(lastSlash + 1) : name;
};
const storage = multer.diskStorage({ const storage = multer.diskStorage({
destination: (req, file, cb) => { destination: (req, file, cb) => {
const subfolder = req.requestUploadSubfolder || ''; const subfolder = req.requestUploadSubfolder || '';
const destinationDir = path.join(REQUESTS_UPLOAD_ROOT, subfolder); const destinationDir = subfolder ? `${REQUESTS_UPLOAD_ROOT}/${subfolder}` : REQUESTS_UPLOAD_ROOT;
ensureDirectory(destinationDir); ensureDirectory(destinationDir);
cb(null, destinationDir); cb(null, destinationDir);
}, },
filename: (req, file, cb) => { filename: (req, file, cb) => {
const extension = path.extname(file.originalname) || ''; const extension = getExtension(file.originalname);
const baseName = path const baseName = getBasename(file.originalname)
.basename(file.originalname, extension)
.replace(/[^a-zA-Z0-9-_]+/g, '_') .replace(/[^a-zA-Z0-9-_]+/g, '_')
.toLowerCase(); .toLowerCase();
cb(null, `${Date.now()}_${baseName}${extension}`); cb(null, `${Date.now()}_${baseName}${extension}`);
@@ -97,7 +107,7 @@ const cleanupUploadedFiles = async (req) => {
const subfolder = req.requestUploadSubfolder || ''; const subfolder = req.requestUploadSubfolder || '';
const removalTasks = req.files.map((file) => { const removalTasks = req.files.map((file) => {
const filePath = path.join(REQUESTS_UPLOAD_ROOT, subfolder, file.filename); const filePath = subfolder ? `${REQUESTS_UPLOAD_ROOT}/${subfolder}/${file.filename}` : `${REQUESTS_UPLOAD_ROOT}/${file.filename}`;
return fs.promises.unlink(filePath).catch((error) => { return fs.promises.unlink(filePath).catch((error) => {
if (error.code !== 'ENOENT') { if (error.code !== 'ENOENT') {
console.error('[Requests] Failed to cleanup uploaded file:', error.message); console.error('[Requests] Failed to cleanup uploaded file:', error.message);
@@ -115,7 +125,7 @@ const mapFilesToMetadata = (req) => {
const subfolder = req.requestUploadSubfolder || ''; const subfolder = req.requestUploadSubfolder || '';
return req.files.map((file) => { return req.files.map((file) => {
const relativePath = path.join('requests', subfolder, file.filename).replace(/\\/g, '/'); const relativePath = subfolder ? `requests/${subfolder}/${file.filename}` : `requests/${file.filename}`;
return { return {
id: `file-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, id: `file-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
name: file.originalname, name: file.originalname,
@@ -159,7 +169,7 @@ const removeStoredFiles = async (files = []) => {
const tasks = files const tasks = files
.filter((file) => file && file.storagePath) .filter((file) => file && file.storagePath)
.map((file) => { .map((file) => {
const absolutePath = path.join(__dirname, '..', '..', 'remote-assets', 'uploads', file.storagePath); const absolutePath = `server/remote-assets/uploads/${file.storagePath}`;
return fs.promises.unlink(absolutePath).catch((error) => { return fs.promises.unlink(absolutePath).catch((error) => {
if (error.code !== 'ENOENT') { if (error.code !== 'ENOENT') {
console.error('[Requests] Failed to remove stored file:', error.message); console.error('[Requests] Failed to remove stored file:', error.message);
@@ -218,7 +228,7 @@ router.get('/received', verifyToken, async (req, res) => {
router.post( router.post(
'/', '/',
verifyToken, verifyToken,
handleFilesUpload('files', (req) => path.join('sent', (req.companyId || 'unknown').toString()), 10), handleFilesUpload('files', (req) => `sent/${(req.companyId || 'unknown').toString()}`, 10),
async (req, res) => { async (req, res) => {
try { try {
const senderCompanyId = req.companyId; const senderCompanyId = req.companyId;
@@ -317,7 +327,7 @@ router.post(
router.put( router.put(
'/:id', '/:id',
verifyToken, verifyToken,
handleFilesUpload('responseFiles', (req) => path.join('responses', req.params.id || 'unknown'), 5), handleFilesUpload('responseFiles', (req) => `responses/${req.params.id || 'unknown'}`, 5),
async (req, res) => { async (req, res) => {
try { try {
const { id } = req.params; const { id } = req.params;

View File

@@ -1,11 +1,10 @@
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const path = require('path');
require('dotenv').config(); require('dotenv').config();
// Импорт моделей // Импорт моделей - прямые пути без path.join и __dirname
const User = require(path.join(__dirname, '..', 'models', 'User')); const User = require('../models/User');
const Company = require(path.join(__dirname, '..', 'models', 'Company')); const Company = require('../models/Company');
const Request = require(path.join(__dirname, '..', 'models', 'Request')); const Request = require('../models/Request');
const primaryUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/procurement_db'; const primaryUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/procurement_db';
const fallbackUri = const fallbackUri =