feature/worker #111
@@ -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;
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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' })
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user