feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок - Для push операций отображается registry вместо платформ - Сохранение пользователя при создании push лога - Исправлена ошибка с logger в push_docker_image endpoint - Улучшено отображение истории сборок с визуальными индикаторами
This commit is contained in:
176
app/templates/pages/playbooks/create.html
Normal file
176
app/templates/pages/playbooks/create.html
Normal file
@@ -0,0 +1,176 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Создать Playbook - DevOpsLab{% endblock %}
|
||||
{% block page_title %}Создать Playbook{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form
|
||||
hx-post="/api/v1/playbooks"
|
||||
hx-target="#playbook-result"
|
||||
hx-swap="innerHTML"
|
||||
id="playbook-form"
|
||||
>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Название playbook</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
class="form-control"
|
||||
placeholder="my-playbook"
|
||||
required
|
||||
pattern="[a-z0-9-]+"
|
||||
title="Только строчные буквы, цифры и дефисы"
|
||||
>
|
||||
<div class="form-text">
|
||||
Только строчные буквы, цифры и дефисы
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Описание</label>
|
||||
<textarea
|
||||
name="description"
|
||||
class="form-control"
|
||||
rows="3"
|
||||
placeholder="Описание playbook..."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Роли</label>
|
||||
<div class="form-text mb-2">
|
||||
Выберите роли для включения в playbook
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
{% for role in roles %}
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
name="roles"
|
||||
value="{{ role }}"
|
||||
id="role-{{ role }}"
|
||||
>
|
||||
<label class="form-check-label" for="role-{{ role }}">
|
||||
{{ role }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if not roles %}
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
Роли не найдены. Создайте роли перед созданием playbook.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Переменные (YAML)</label>
|
||||
<textarea
|
||||
name="variables"
|
||||
id="variables-editor"
|
||||
class="form-control font-monospace"
|
||||
rows="10"
|
||||
placeholder="vars:
|
||||
key1: value1
|
||||
key2: value2"
|
||||
></textarea>
|
||||
<div class="form-text">
|
||||
Переменные в формате YAML (опционально)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Инвентарь (YAML)</label>
|
||||
<textarea
|
||||
name="inventory"
|
||||
id="inventory-editor"
|
||||
class="form-control font-monospace"
|
||||
rows="10"
|
||||
placeholder="all:
|
||||
hosts:
|
||||
host1:
|
||||
ansible_host: 192.168.1.10
|
||||
host2:
|
||||
ansible_host: 192.168.1.11"
|
||||
></textarea>
|
||||
<div class="form-text">
|
||||
Инвентарь в формате YAML (опционально)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="playbook-result"></div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save me-2"></i>
|
||||
Создать playbook
|
||||
</button>
|
||||
<a href="/playbooks" class="btn btn-secondary">
|
||||
<i class="fas fa-times me-2"></i>
|
||||
Отмена
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Инициализация редакторов
|
||||
if (typeof CodeEditor !== 'undefined') {
|
||||
const varsEditor = CodeEditor.init('variables-editor', 'yaml');
|
||||
const invEditor = CodeEditor.init('inventory-editor', 'yaml');
|
||||
|
||||
// Валидация при изменении
|
||||
if (varsEditor) {
|
||||
varsEditor.on('change', function() {
|
||||
const content = varsEditor.getValue();
|
||||
if (content.trim()) {
|
||||
const validation = CodeEditor.validateYAML(content);
|
||||
if (!validation.valid) {
|
||||
CodeEditor.showErrors(varsEditor, validation.errors);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (invEditor) {
|
||||
invEditor.on('change', function() {
|
||||
const content = invEditor.getValue();
|
||||
if (content.trim()) {
|
||||
const validation = CodeEditor.validateYAML(content);
|
||||
if (!validation.valid) {
|
||||
CodeEditor.showErrors(invEditor, validation.errors);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Обработка формы
|
||||
const form = document.getElementById('playbook-form');
|
||||
form.addEventListener('htmx:afterRequest', function(event) {
|
||||
if (event.detail.xhr.status === 200) {
|
||||
const response = JSON.parse(event.detail.xhr.responseText);
|
||||
window.location.href = `/playbooks/${response.id}`;
|
||||
} else {
|
||||
const resultDiv = document.getElementById('playbook-result');
|
||||
try {
|
||||
const response = JSON.parse(event.detail.xhr.responseText);
|
||||
resultDiv.innerHTML = `<div class="alert alert-danger mt-3">${response.detail || 'Ошибка при создании playbook'}</div>`;
|
||||
} catch (e) {
|
||||
resultDiv.innerHTML = '<div class="alert alert-danger mt-3">Ошибка при создании playbook</div>';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user