Files
DevOpsLab/app/static/js/editor.js
Сергей Антропов 1fbf9185a2 feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок
- Для push операций отображается registry вместо платформ
- Сохранение пользователя при создании push лога
- Исправлена ошибка с logger в push_docker_image endpoint
- Улучшено отображение истории сборок с визуальными индикаторами
2026-02-15 22:59:02 +03:00

191 lines
6.2 KiB
JavaScript
Raw Permalink 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.

/**
* Редактор кода с подсветкой синтаксиса
* Использует CodeMirror 6
* Автор: Сергей Антропов
* Сайт: https://devops.org.ru
*/
// Инициализация CodeMirror редактора
function initCodeEditor(textareaId, language = 'yaml', options = {}) {
const textarea = document.getElementById(textareaId);
if (!textarea) {
console.error(`Textarea with id "${textareaId}" not found`);
return null;
}
// Проверяем, что CodeMirror загружен
if (typeof CodeMirror === 'undefined') {
console.error('CodeMirror is not loaded. Please include CodeMirror library.');
return null;
}
const defaultOptions = {
lineNumbers: true,
mode: language,
theme: 'default',
indentUnit: 2,
indentWithTabs: false,
lineWrapping: true,
matchBrackets: true,
autoCloseBrackets: true,
foldGutter: true,
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
...options
};
const editor = CodeMirror.fromTextArea(textarea, defaultOptions);
// Сохраняем значение обратно в textarea при изменении
editor.on('change', function(cm) {
cm.save();
});
return editor;
}
// Валидация YAML
function validateYAML(content) {
try {
// Используем js-yaml если доступен, иначе простую проверку
if (typeof jsyaml !== 'undefined') {
jsyaml.load(content);
return { valid: true, errors: [] };
} else {
// Простая проверка синтаксиса
const lines = content.split('\n');
const errors = [];
let indentStack = [0];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
// Пропускаем пустые строки и комментарии
if (!trimmed || trimmed.startsWith('#')) {
continue;
}
// Проверка отступов
const indent = line.length - line.trimStart().length;
const lastIndent = indentStack[indentStack.length - 1];
if (indent > lastIndent + 2) {
errors.push({
line: i + 1,
message: `Неправильный отступ на строке ${i + 1}`
});
}
// Проверка ключей без значений
if (trimmed.endsWith(':') && !trimmed.includes('{') && !trimmed.includes('[')) {
// Это нормально для YAML
}
}
return { valid: errors.length === 0, errors: errors };
}
} catch (e) {
return {
valid: false,
errors: [{
line: 1,
message: e.message
}]
};
}
}
// Валидация Ansible playbook
function validateAnsiblePlaybook(content) {
const yamlValidation = validateYAML(content);
if (!yamlValidation.valid) {
return yamlValidation;
}
try {
if (typeof jsyaml !== 'undefined') {
const parsed = jsyaml.load(content);
if (!Array.isArray(parsed)) {
return {
valid: false,
errors: [{
line: 1,
message: 'Playbook должен быть списком (массивом)'
}]
};
}
const errors = [];
parsed.forEach((play, index) => {
if (!play.hosts && !play.tasks && !play.roles) {
errors.push({
line: index + 1,
message: `Play ${index + 1}: должен содержать hosts, tasks или roles`
});
}
});
return { valid: errors.length === 0, errors: errors };
}
} catch (e) {
return {
valid: false,
errors: [{
line: 1,
message: e.message
}]
};
}
return { valid: true, errors: [] };
}
// Показ ошибок валидации в редакторе
function showValidationErrors(editor, errors) {
if (!editor || !editor._validationMarkers) {
return;
}
// Очищаем предыдущие маркеры
editor._validationMarkers.forEach(marker => {
if (marker && typeof marker.clear === 'function') {
marker.clear();
} else if (marker && typeof marker.remove === 'function') {
marker.remove();
}
});
editor._validationMarkers = [];
// Очищаем все gutter маркеры
editor.clearGutter('CodeMirror-linenumbers');
errors.forEach(error => {
const line = Math.max(0, (error.line || 1) - 1); // CodeMirror использует 0-based индексы
try {
// Добавляем класс для подсветки строки с ошибкой
editor.addLineClass(line, 'background', 'cm-error-line');
// Добавляем маркер в gutter
const marker = document.createElement('span');
marker.className = 'cm-error-marker';
marker.textContent = '⚠';
marker.title = error.message || 'Ошибка';
editor.setGutterMarker(line, 'CodeMirror-linenumbers', marker);
editor._validationMarkers.push({ line: line, marker: marker });
} catch (e) {
console.warn('Error adding validation marker:', e);
}
});
}
// Экспорт функций
window.CodeEditor = {
init: initCodeEditor,
validateYAML: validateYAML,
validateAnsible: validateAnsiblePlaybook,
showErrors: showValidationErrors
};