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

View File

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

View File

@@ -1,11 +1,10 @@
const express = require('express')
const fs = require('fs')
const path = require('path')
const router = express.Router()
const BuyDocument = require('../models/BuyDocument')
// 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)) {
fs.mkdirSync(docsDir, { recursive: true })
}
@@ -57,7 +56,7 @@ router.post('/docs', async (req, res) => {
// Save file to disk
const binaryData = Buffer.from(fileData, 'base64')
const filePath = path.join(docsDir, `${id}.${type}`)
const filePath = `${docsDir}/${id}.${type}`
fs.writeFileSync(filePath, binaryData)
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' })
}
const filePath = path.join(docsDir, `${id}.${doc.type}`)
const filePath = `${docsDir}/${id}.${doc.type}`
if (!fs.existsSync(filePath)) {
console.log('[BUY API] File not found on disk:', filePath)
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 { verifyToken } = require('../middleware/auth');
const BuyProduct = require('../models/BuyProduct');
const path = require('path');
const fs = require('fs');
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) => {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
@@ -24,17 +23,28 @@ const ALLOWED_MIME_TYPES = new Set([
'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({
destination: (req, file, cb) => {
const productId = req.params.id || 'common';
const productDir = path.join(UPLOADS_ROOT, productId);
const productDir = `${UPLOADS_ROOT}/${productId}`;
ensureDirectory(productDir);
cb(null, productDir);
},
filename: (req, file, cb) => {
const originalExtension = path.extname(file.originalname) || '';
const baseName = path
.basename(file.originalname, originalExtension)
const originalExtension = getExtension(file.originalname);
const baseName = getBasename(file.originalname)
.replace(/[^a-zA-Z0-9-_]+/g, '_')
.toLowerCase();
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' });
}
const relativePath = path.join('buy-products', id, req.file.filename).replace(/\\/g, '/');
const relativePath = `buy-products/${id}/${req.file.filename}`;
const file = {
id: `file-${Date.now()}`,
name: req.file.originalname,
@@ -293,7 +303,7 @@ router.delete('/:id/files/:fileId', verifyToken, async (req, res) => {
await product.save();
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) => {
if (unlinkError && unlinkError.code !== 'ENOENT') {

View File

@@ -3,7 +3,6 @@ const router = express.Router();
const { verifyToken } = require('../middleware/auth');
const Request = require('../models/Request');
const BuyProduct = require('../models/BuyProduct');
const path = require('path');
const fs = require('fs');
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) => {
if (!fs.existsSync(dirPath)) {
@@ -38,17 +37,28 @@ const ALLOWED_REQUEST_MIME_TYPES = new Set([
'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({
destination: (req, file, cb) => {
const subfolder = req.requestUploadSubfolder || '';
const destinationDir = path.join(REQUESTS_UPLOAD_ROOT, subfolder);
const destinationDir = subfolder ? `${REQUESTS_UPLOAD_ROOT}/${subfolder}` : REQUESTS_UPLOAD_ROOT;
ensureDirectory(destinationDir);
cb(null, destinationDir);
},
filename: (req, file, cb) => {
const extension = path.extname(file.originalname) || '';
const baseName = path
.basename(file.originalname, extension)
const extension = getExtension(file.originalname);
const baseName = getBasename(file.originalname)
.replace(/[^a-zA-Z0-9-_]+/g, '_')
.toLowerCase();
cb(null, `${Date.now()}_${baseName}${extension}`);
@@ -97,7 +107,7 @@ const cleanupUploadedFiles = async (req) => {
const subfolder = req.requestUploadSubfolder || '';
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) => {
if (error.code !== 'ENOENT') {
console.error('[Requests] Failed to cleanup uploaded file:', error.message);
@@ -115,7 +125,7 @@ const mapFilesToMetadata = (req) => {
const subfolder = req.requestUploadSubfolder || '';
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 {
id: `file-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
name: file.originalname,
@@ -159,7 +169,7 @@ const removeStoredFiles = async (files = []) => {
const tasks = files
.filter((file) => file && file.storagePath)
.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) => {
if (error.code !== 'ENOENT') {
console.error('[Requests] Failed to remove stored file:', error.message);
@@ -218,7 +228,7 @@ router.get('/received', verifyToken, async (req, res) => {
router.post(
'/',
verifyToken,
handleFilesUpload('files', (req) => path.join('sent', (req.companyId || 'unknown').toString()), 10),
handleFilesUpload('files', (req) => `sent/${(req.companyId || 'unknown').toString()}`, 10),
async (req, res) => {
try {
const senderCompanyId = req.companyId;
@@ -317,7 +327,7 @@ router.post(
router.put(
'/:id',
verifyToken,
handleFilesUpload('responseFiles', (req) => path.join('responses', req.params.id || 'unknown'), 5),
handleFilesUpload('responseFiles', (req) => `responses/${req.params.id || 'unknown'}`, 5),
async (req, res) => {
try {
const { id } = req.params;

View File

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