klg-asutk-app/scripts/update-manifest.js
Yuriy b147d16798 MVP: заглушки, auth, .env.example, связь с бэкендом, главная КЛГ
- Заполнены заглушки: user-friendly-messages, health, aria, keyboard
- backend: core/auth.py, /api/v1/stats; cached-api → backend-client при USE_MOCK_DATA=false
- .env.example, middleware auth (skip при USE_MOCK_DATA), убраны неиспользуемые deps
- Страницы: airworthiness, maintenance, defects, modifications; AircraftAddModal, Sidebar
- Главная страница: REFLY — Контроль лётной годности (вместо Numerology App)
- Линт/скрипты: eslintrc, security, cleanup, logs, api inbox/knowledge

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 16:43:53 +03:00

171 lines
5.6 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Скрипт для добавления всех файлов из knowledge/ в manifest.json
* Приводит все записи к единому формату с обязательными полями
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const MANIFEST_PATH = path.join(__dirname, '../index/manifest.json');
const KNOWLEDGE_DIR = path.join(__dirname, '../knowledge');
// Ключевые документы для статуса approved (3-5 документов)
const KEY_DOCUMENTS_PATTERNS = [
/papa_project_bootstrap/i,
/145\.1в/i,
/рц-ап-145/i,
/tv3_117_kniga_1.*turboval/i,
/tech_cards_registry/i
];
function getDocumentType(filePath) {
const dir = path.dirname(filePath).split(path.sep).pop();
if (dir === 'reglaments') return 'reglament';
if (dir === 'guides') return 'template';
if (dir === 'samples') return 'sample';
return 'reglament';
}
function getDomain(filePath, fileName) {
const lowerName = fileName.toLowerCase();
if (lowerName.includes('tmc') || lowerName.includes('tv3') || lowerName.includes('двигател') || lowerName.includes('byulleten')) return 'tmc';
if (lowerName.includes('145') || lowerName.includes('183') || lowerName.includes('регламент') || lowerName.includes('руководство')) return 'regulations';
if (lowerName.includes('анкет') || lowerName.includes('hr')) return 'hr';
if (lowerName.includes('инспекц')) return 'inspection';
return 'core';
}
function generateId(type, index) {
const prefix = type === 'reglament' ? 'reg' : type === 'template' ? 'guide' : 'sample';
return `${prefix}-${String(index).padStart(3, '0')}`;
}
function getTitle(fileName) {
// Убираем расширение
let title = fileName.replace(/\.[^.]+$/, '');
// Убираем "— копия" и подобное
title = title.replace(/\s*—\s*копия\s*/gi, '').trim();
// Если название слишком длинное, обрезаем
if (title.length > 100) {
title = title.substring(0, 97) + '...';
}
return title || fileName;
}
function isKeyDocument(fileName) {
return KEY_DOCUMENTS_PATTERNS.some(pattern => pattern.test(fileName));
}
function getAllFiles() {
const files = [];
['reglaments', 'guides', 'samples'].forEach(dir => {
const dirPath = path.join(KNOWLEDGE_DIR, dir);
if (!fs.existsSync(dirPath)) return;
const dirFiles = fs.readdirSync(dirPath)
.filter(file => {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
return stat.isFile() && file !== '.DS_Store' && file !== 'README.md';
})
.map(file => ({
fileName: file,
filePath: path.join(dir, file),
fullPath: path.join(dirPath, file)
}));
files.push(...dirFiles);
});
return files;
}
function updateManifest() {
// console.log('🔍 Сканирование файлов в knowledge/...\n');
// Получаем все файлы
const allFiles = getAllFiles();
console.log(`Найдено файлов: ${allFiles.length}\n`);
// Генерируем документы для всех файлов
const documents = [];
let regIndex = 1;
let guideIndex = 1;
let sampleIndex = 1;
// Сортируем файлы для консистентности
allFiles.sort((a, b) => {
const typeA = getDocumentType(a.filePath);
const typeB = getDocumentType(b.filePath);
const typeOrder = { reglament: 0, template: 1, sample: 2 };
if (typeOrder[typeA] !== typeOrder[typeB]) {
return typeOrder[typeA] - typeOrder[typeB];
}
return a.fileName.localeCompare(b.fileName);
});
allFiles.forEach(file => {
const relativePath = `knowledge/${file.filePath}`;
const type = getDocumentType(relativePath);
// Генерируем ID
let id;
if (type === 'reglament') {
id = generateId('reglament', regIndex++);
} else if (type === 'template') {
id = generateId('template', guideIndex++);
} else {
id = generateId('sample', sampleIndex++);
}
const doc = {
id,
title: getTitle(file.fileName),
path: relativePath,
type,
domain: getDomain(relativePath, file.fileName),
version: '1.0.0',
status: isKeyDocument(file.fileName) ? 'approved' : 'draft'
};
documents.push(doc);
});
// Читаем текущий manifest
const manifest = JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8'));
// Обновляем manifest
manifest.documents = documents;
manifest.manifest.lastUpdated = new Date().toISOString();
// Обновляем метаданные
const statusBreakdown = { approved: 0, draft: 0, deprecated: 0 };
const fileTypes = {};
documents.forEach(doc => {
statusBreakdown[doc.status] = (statusBreakdown[doc.status] || 0) + 1;
const ext = path.extname(doc.path).slice(1).toLowerCase();
fileTypes[ext] = (fileTypes[ext] || 0) + 1;
});
manifest.metadata.documentCount = documents.length;
manifest.metadata.statusBreakdown = statusBreakdown;
manifest.metadata.fileTypes = fileTypes;
// Сохраняем
fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2) + '\n', 'utf8');
console.log(`✅ Обновлено документов: ${documents.length}`);
console.log(` - Approved: ${statusBreakdown.approved}`);
console.log(` - Draft: ${statusBreakdown.draft}`);
console.log(` - Deprecated: ${statusBreakdown.deprecated}\n`);
}
updateManifest();