mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-29 01:37:17 +00:00
implement logs support in interface
This commit is contained in:
parent
88c9b29e74
commit
abd77e655b
@ -40,7 +40,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table id="packages" class="table table-striped table-hover"
|
<table id="packages" class="table table-striped table-hover"
|
||||||
data-click-to-select="true"
|
|
||||||
data-export-options='{"fileName": "packages"}'
|
data-export-options='{"fileName": "packages"}'
|
||||||
data-page-list="[10, 25, 50, 100, all]"
|
data-page-list="[10, 25, 50, 100, all]"
|
||||||
data-page-size="10"
|
data-page-size="10"
|
||||||
@ -76,14 +75,14 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources">ahriman</a></li>
|
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources"><i class="bi bi-github"></i> ahriman</a></li>
|
||||||
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
|
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
|
||||||
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
|
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{% if index_url is not none %}
|
{% if index_url is not none %}
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a class="nav-link" href="{{ index_url }}" title="repo index">repo index</a></li>
|
<li><a class="nav-link" href="{{ index_url }}" title="repo index"><i class="bi bi-house"></i> repo index</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@ -92,7 +91,7 @@
|
|||||||
{{ auth.control|safe }}
|
{{ auth.control|safe }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<form action="/api/v1/logout" method="post">
|
<form action="/api/v1/logout" method="post">
|
||||||
<button class="btn btn-link" style="text-decoration: none">logout ({{ auth.username }})</button>
|
<button class="btn btn-link" style="text-decoration: none"><i class="bi bi-box-arrow-right"></i> logout ({{ auth.username }})</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -110,6 +109,8 @@
|
|||||||
|
|
||||||
{% include "build-status/package-add-modal.jinja2" %}
|
{% include "build-status/package-add-modal.jinja2" %}
|
||||||
|
|
||||||
|
{% include "build-status/package-info-modal.jinja2" %}
|
||||||
|
|
||||||
{% include "build-status/table.jinja2" %}
|
{% include "build-status/table.jinja2" %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
<div id="failed-form" tabindex="-1" role="dialog" class="modal fade">
|
<div id="failed-form" tabindex="-1" role="dialog" class="modal fade">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header bg-danger">
|
<div class="modal-header bg-danger text-white">
|
||||||
<h4 class="modal-title">failed</h4>
|
<h4 id="error-title" class="modal-title"></h4>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Packages update has failed.</p>
|
<p id="error-description"></p>
|
||||||
<p id="error-details"></p>
|
<p id="error-details"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">close</button>
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i> close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -18,10 +18,14 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
const failedForm = $("#failed-form");
|
const failedForm = $("#failed-form");
|
||||||
|
const errorDescription = $("#error-description");
|
||||||
const errorDetails = $("#error-details");
|
const errorDetails = $("#error-details");
|
||||||
|
const errorTitle = $("#error-title");
|
||||||
failedForm.on("hidden.bs.modal", () => { reload(); });
|
failedForm.on("hidden.bs.modal", () => { reload(); });
|
||||||
|
|
||||||
function showFailure(details) {
|
function showFailure(title, description, details) {
|
||||||
|
errorTitle.text(title);
|
||||||
|
errorDescription.text(description);
|
||||||
errorDetails.text(details);
|
errorDetails.text(details);
|
||||||
failedForm.modal("show");
|
failedForm.modal("show");
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<form action="/api/v1/login" method="post">
|
<form action="/api/v1/login" method="post">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">login</h4>
|
<h4 class="modal-title">Login</h4>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary">login</button>
|
<button class="btn btn-primary"><i class="bi bi-person"></i> login</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">add new packages</h4>
|
<h4 class="modal-title">Add new packages</h4>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
@ -15,9 +15,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">close</button>
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addPackages()"><i class="bi bi-play"></i> add</button>
|
||||||
<button type="button" class="btn btn-success" data-bs-dismiss="modal" onclick="requestPackages()">request</button>
|
<button type="button" class="btn btn-success" data-bs-dismiss="modal" onclick="requestPackages()"><i class="bi bi-plus"></i> request</button>
|
||||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addPackages()">add</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><i class="bi bi-x"></i> close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
<div id="package-info-form" tabindex="-1" role="dialog" class="modal fade">
|
||||||
|
<div class="modal-dialog modal-xl" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div id="package-info-modal-header" class="modal-header">
|
||||||
|
<h4 id="package-info" class="modal-title"></h4>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<pre class="pre-scrollable language-logs"><code id="package-info-logs" class="language-logs"></code><button id="copy-btn" type="button" class="btn language-logs" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="showLogs()"><i class="bi bi-arrow-clockwise"></i> reload</button>
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i> close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const packageInfo = $("#package-info");
|
||||||
|
const packageInfoForm = $("#package-info-form");
|
||||||
|
const packageInfoHeader = $("#package-info-modal-header");
|
||||||
|
const packageInfoLogs = $("#package-info-logs");
|
||||||
|
const packageInfoLogsCopyButton = $("#copy-btn");
|
||||||
|
|
||||||
|
async function copyLogs() {
|
||||||
|
const logs = packageInfoLogs.text();
|
||||||
|
await navigator.clipboard.writeText(logs);
|
||||||
|
|
||||||
|
packageInfoLogsCopyButton.html("<i class=\"bi bi-clipboard-check\"></i> copied");
|
||||||
|
setTimeout(()=> {
|
||||||
|
packageInfoLogsCopyButton.html("<i class=\"bi bi-clipboard\"></i> copy");
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showLogs(package) {
|
||||||
|
const isPackageBaseSet = package !== undefined;
|
||||||
|
if (isPackageBaseSet)
|
||||||
|
packageInfoForm.data("package", package); // set package base as currently used
|
||||||
|
else
|
||||||
|
package = packageInfoForm.data("package"); // read package base from the current window attribute
|
||||||
|
|
||||||
|
const headerClass = status => {
|
||||||
|
if (status === "pending") return ["bg-warning"];
|
||||||
|
if (status === "building") return ["bg-warning"];
|
||||||
|
if (status === "failed") return ["bg-danger", "text-white"];
|
||||||
|
if (status === "success") return ["bg-success", "text-white"];
|
||||||
|
return ["bg-secondary", "text-white"];
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: `/api/v1/packages/${package}/logs`,
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
success: response => {
|
||||||
|
packageInfo.text(`${response.package_base} ${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOString()}`);
|
||||||
|
packageInfoLogs.text(response.logs);
|
||||||
|
|
||||||
|
packageInfoHeader.removeClass();
|
||||||
|
packageInfoHeader.addClass("modal-header");
|
||||||
|
headerClass(response.status.status).forEach((clz) => packageInfoHeader.addClass(clz));
|
||||||
|
|
||||||
|
if (isPackageBaseSet) packageInfoForm.modal("show"); // we don't need to show window again
|
||||||
|
},
|
||||||
|
error: (jqXHR, _, errorThrown) => {
|
||||||
|
// show failed modal in case if first time loading
|
||||||
|
if (isPackageBaseSet) showFailure("Load failure", `Could not load package ${package} logs:`, errorThrown);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,16 +1,16 @@
|
|||||||
<div id="success-form" tabindex="-1" role="dialog" class="modal fade">
|
<div id="success-form" tabindex="-1" role="dialog" class="modal fade">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header bg-success">
|
<div class="modal-header bg-success text-white">
|
||||||
<h4 class="modal-title">success</h4>
|
<h4 id="success-title" class="modal-title"></h4>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Packages update has been run.</p>
|
<p id="success-description"></p>
|
||||||
<ul id="success-details"></ul>
|
<ul id="success-details"></ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">close</button>
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i> close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -18,10 +18,14 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
const successForm = $("#success-form");
|
const successForm = $("#success-form");
|
||||||
|
const successDescription = $("#success-description");
|
||||||
const successDetails = $("#success-details");
|
const successDetails = $("#success-details");
|
||||||
|
const successTitle = $("#success-title");
|
||||||
successForm.on("hidden.bs.modal", () => { reload(); });
|
successForm.on("hidden.bs.modal", () => { reload(); });
|
||||||
|
|
||||||
function showSuccess(details) {
|
function showSuccess(title, description, details) {
|
||||||
|
successTitle.text(title);
|
||||||
|
successDescription.text(description);
|
||||||
successDetails.empty().append(details);
|
successDetails.empty().append(details);
|
||||||
successForm.modal("show");
|
successForm.modal("show");
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
() => {
|
() => {
|
||||||
removeButton.prop("disabled", !table.bootstrapTable("getSelections").length);
|
removeButton.prop("disabled", !table.bootstrapTable("getSelections").length);
|
||||||
});
|
});
|
||||||
|
table.on("click-row.bs.table", (_, row) => { showLogs(row.id); });
|
||||||
|
|
||||||
const architectureBadge = $("#badge-architecture");
|
const architectureBadge = $("#badge-architecture");
|
||||||
const repositoryBadge = $("#badge-repository");
|
const repositoryBadge = $("#badge-repository");
|
||||||
@ -26,9 +27,11 @@
|
|||||||
li.innerText = pkg;
|
li.innerText = pkg;
|
||||||
return li;
|
return li;
|
||||||
});
|
});
|
||||||
showSuccess(details);
|
showSuccess("Success", `Package action at ${uri} has been run on:`, details);
|
||||||
|
},
|
||||||
|
error: (jqXHR, _, errorThrown) => {
|
||||||
|
showFailure("Action failed", `Package action request at ${uri} on ${packages} has failed:`, errorThrown);
|
||||||
},
|
},
|
||||||
error: (jqXHR, _, errorThrown) => { showFailure(errorThrown); },
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +41,11 @@
|
|||||||
|
|
||||||
function removePackages() { doPackageAction("/api/v1/service/remove", getSelection()); }
|
function removePackages() { doPackageAction("/api/v1/service/remove", getSelection()); }
|
||||||
|
|
||||||
function updatePackages() { doPackageAction("/api/v1/service/add", getSelection()); }
|
function updatePackages() {
|
||||||
|
const currentSelection = getSelection();
|
||||||
|
const url = currentSelection.length === 0 ? "/api/v1/service/update" : "/api/v1/service/add";
|
||||||
|
doPackageAction(url, getSelection());
|
||||||
|
}
|
||||||
|
|
||||||
function hideControls(hidden) {
|
function hideControls(hidden) {
|
||||||
addButton.attr("hidden", hidden);
|
addButton.attr("hidden", hidden);
|
||||||
@ -95,7 +102,7 @@
|
|||||||
table.bootstrapTable("hideLoading");
|
table.bootstrapTable("hideLoading");
|
||||||
} else {
|
} else {
|
||||||
// other errors
|
// other errors
|
||||||
showFailure(errorThrown);
|
showFailure("Load failure", "Could not load list of packages:", errorThrown);
|
||||||
}
|
}
|
||||||
hideControls(true);
|
hideControls(true);
|
||||||
},
|
},
|
||||||
|
@ -19,10 +19,11 @@
|
|||||||
<p>This repository is signed with <a href="https://pgp.mit.edu/pks/lookup?search=0x{{ pgp_key }}&fingerprint=on&op=index" title="key search">{{ pgp_key }}</a> by default.</p>
|
<p>This repository is signed with <a href="https://pgp.mit.edu/pks/lookup?search=0x{{ pgp_key }}&fingerprint=on&op=index" title="key search">{{ pgp_key }}</a> by default.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<pre>$ cat /etc/pacman.conf
|
<p>In order to use this repository edit your <code>/etc/pacman.conf</code> as following:</p>
|
||||||
[{{ repository }}]
|
|
||||||
|
<pre class="language-ini"><code id="pacman-conf" class="language-ini">[{{ repository }}]
|
||||||
Server = {{ link_path }}
|
Server = {{ link_path }}
|
||||||
SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Package{% if has_package_signed %}Required{% else %}Never{% endif %} TrustedOnly</pre>
|
SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Package{% if has_package_signed %}Required{% else %}Never{% endif %} TrustedOnly</code><button id="copy-btn" type="button" class="btn language-ini" onclick="copyPacmanConf()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -83,17 +84,32 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
|
|||||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
{% if homepage is not none %}
|
{% if homepage is not none %}
|
||||||
<li><a class="nav-link" href="{{ homepage }}" title="homepage">homepage</a></li>
|
<li><a class="nav-link" href="{{ homepage }}" title="homepage"><i class="bi bi-house"></i> homepage</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources">ahriman</a></li>
|
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources"><i class="bi bi-github"></i> ahriman</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include "utils/bootstrap-scripts.jinja2" %}
|
{% include "utils/bootstrap-scripts.jinja2" %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const pacmanConf = $("#pacman-conf");
|
||||||
|
const pacmanConfCopyButton = $("#copy-btn");
|
||||||
|
|
||||||
|
async function copyPacmanConf() {
|
||||||
|
const conf = pacmanConf.text();
|
||||||
|
await navigator.clipboard.writeText(conf);
|
||||||
|
|
||||||
|
pacmanConfCopyButton.html("<i class=\"bi bi-clipboard-check\"></i> copied");
|
||||||
|
setTimeout(() => {
|
||||||
|
pacmanConfCopyButton.html("<i class=\"bi bi-clipboard\"></i> copy");
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
|
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js" integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3" crossorigin="anonymous"></script>
|
||||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js" integrity="sha384-IDwe1+LCz02ROU9k972gdyvl+AESN10+x7tBKgc9I5HFtuNz0wWnPclzo6p9vxnk" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://unpkg.com/bootstrap-table@1.21.1/dist/bootstrap-table.min.js"></script>
|
||||||
|
|
||||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
<script src="https://unpkg.com/bootstrap-table@1.21.1/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||||
|
|
||||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
<script src="https://unpkg.com/bootstrap-table@1.21.1/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous" type="text/css">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.css" type="text/css">
|
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.21.1/dist/bootstrap-table.min.css" type="text/css">
|
||||||
|
|
||||||
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet">
|
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet">
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.pre-scrollable {
|
||||||
|
max-height: 680px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"] {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"] button{
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
1
setup.py
1
setup.py
@ -74,6 +74,7 @@ setup(
|
|||||||
"package/share/ahriman/templates/build-status/failed-modal.jinja2",
|
"package/share/ahriman/templates/build-status/failed-modal.jinja2",
|
||||||
"package/share/ahriman/templates/build-status/login-modal.jinja2",
|
"package/share/ahriman/templates/build-status/login-modal.jinja2",
|
||||||
"package/share/ahriman/templates/build-status/package-add-modal.jinja2",
|
"package/share/ahriman/templates/build-status/package-add-modal.jinja2",
|
||||||
|
"package/share/ahriman/templates/build-status/package-info-modal.jinja2",
|
||||||
"package/share/ahriman/templates/build-status/success-modal.jinja2",
|
"package/share/ahriman/templates/build-status/success-modal.jinja2",
|
||||||
"package/share/ahriman/templates/build-status/table.jinja2",
|
"package/share/ahriman/templates/build-status/table.jinja2",
|
||||||
]),
|
]),
|
||||||
|
@ -62,7 +62,7 @@ class Auth(LazyLogging):
|
|||||||
Returns:
|
Returns:
|
||||||
str: login control as html code to insert
|
str: login control as html code to insert
|
||||||
"""
|
"""
|
||||||
return """<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#loginForm" style="text-decoration: none">login</button>"""
|
return """<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#loginForm" style="text-decoration: none"><i class="bi bi-box-arrow-in-right"></i> login</button>"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls: Type[Auth], configuration: Configuration, database: SQLite) -> Auth:
|
def load(cls: Type[Auth], configuration: Configuration, database: SQLite) -> Auth:
|
||||||
|
@ -69,7 +69,7 @@ class OAuth(Mapping):
|
|||||||
Returns:
|
Returns:
|
||||||
str: login control as html code to insert
|
str: login control as html code to insert
|
||||||
"""
|
"""
|
||||||
return """<a class="nav-link" href="/api/v1/login" title="login via OAuth2">login</a>"""
|
return """<a class="nav-link" href="/api/v1/login" title="login via OAuth2"><i class="bi bi-google"></i> login</a>"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_provider(name: str) -> Type[aioauth_client.OAuth2Client]:
|
def get_provider(name: str) -> Type[aioauth_client.OAuth2Client]:
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||||
|
from aiohttp.web_exceptions import HTTPNotFound
|
||||||
|
|
||||||
|
from ahriman.core.exceptions import UnknownPackageError
|
||||||
from ahriman.models.log_record_id import LogRecordId
|
from ahriman.models.log_record_id import LogRecordId
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.base import BaseView
|
from ahriman.web.views.base import BaseView
|
||||||
@ -58,10 +60,16 @@ class LogsView(BaseView):
|
|||||||
Response: 200 with package logs on success
|
Response: 200 with package logs on success
|
||||||
"""
|
"""
|
||||||
package_base = self.request.match_info["package"]
|
package_base = self.request.match_info["package"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
_, status = self.service.get(package_base)
|
||||||
|
except UnknownPackageError:
|
||||||
|
raise HTTPNotFound()
|
||||||
logs = self.service.get_logs(package_base)
|
logs = self.service.get_logs(package_base)
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
"package_base": package_base,
|
"package_base": package_base,
|
||||||
|
"status": status.view(),
|
||||||
"logs": logs
|
"logs": logs
|
||||||
}
|
}
|
||||||
return json_response(response)
|
return json_response(response)
|
||||||
|
@ -327,6 +327,7 @@ def test_walk(resource_path_root: Path) -> None:
|
|||||||
resource_path_root / "web" / "templates" / "build-status" / "failed-modal.jinja2",
|
resource_path_root / "web" / "templates" / "build-status" / "failed-modal.jinja2",
|
||||||
resource_path_root / "web" / "templates" / "build-status" / "login-modal.jinja2",
|
resource_path_root / "web" / "templates" / "build-status" / "login-modal.jinja2",
|
||||||
resource_path_root / "web" / "templates" / "build-status" / "package-add-modal.jinja2",
|
resource_path_root / "web" / "templates" / "build-status" / "package-add-modal.jinja2",
|
||||||
|
resource_path_root / "web" / "templates" / "build-status" / "package-info-modal.jinja2",
|
||||||
resource_path_root / "web" / "templates" / "build-status" / "success-modal.jinja2",
|
resource_path_root / "web" / "templates" / "build-status" / "success-modal.jinja2",
|
||||||
resource_path_root / "web" / "templates" / "build-status" / "table.jinja2",
|
resource_path_root / "web" / "templates" / "build-status" / "table.jinja2",
|
||||||
resource_path_root / "web" / "templates" / "static" / "favicon.ico",
|
resource_path_root / "web" / "templates" / "static" / "favicon.ico",
|
||||||
|
@ -2,6 +2,7 @@ import pytest
|
|||||||
|
|
||||||
from aiohttp.test_utils import TestClient
|
from aiohttp.test_utils import TestClient
|
||||||
|
|
||||||
|
from ahriman.models.build_status import BuildStatusEnum
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.status.logs import LogsView
|
from ahriman.web.views.status.logs import LogsView
|
||||||
@ -23,6 +24,11 @@ async def test_delete(client: TestClient, package_ahriman: Package, package_pyth
|
|||||||
"""
|
"""
|
||||||
must delete logs for package
|
must delete logs for package
|
||||||
"""
|
"""
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||||
|
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||||
|
await client.post(f"/api/v1/packages/{package_python_schedule.base}",
|
||||||
|
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
|
||||||
|
|
||||||
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
||||||
json={"created": 0.001, "message": "message", "process_id": 42})
|
json={"created": 0.001, "message": "message", "process_id": 42})
|
||||||
await client.post(f"/api/v1/packages/{package_python_schedule.base}/logs",
|
await client.post(f"/api/v1/packages/{package_python_schedule.base}/logs",
|
||||||
@ -44,6 +50,8 @@ async def test_get(client: TestClient, package_ahriman: Package) -> None:
|
|||||||
"""
|
"""
|
||||||
must get logs for package
|
must get logs for package
|
||||||
"""
|
"""
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||||
|
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||||
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
||||||
json={"created": 0.001, "message": "message", "process_id": 42})
|
json={"created": 0.001, "message": "message", "process_id": 42})
|
||||||
|
|
||||||
@ -51,20 +59,31 @@ async def test_get(client: TestClient, package_ahriman: Package) -> None:
|
|||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
logs = await response.json()
|
logs = await response.json()
|
||||||
assert logs == {"package_base": package_ahriman.base, "logs": "message"}
|
assert logs["logs"] == "message"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_not_foud(client: TestClient, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must return not found for missing package
|
||||||
|
"""
|
||||||
|
response = await client.get(f"/api/v1/packages/{package_ahriman.base}/logs")
|
||||||
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
async def test_post(client: TestClient, package_ahriman: Package) -> None:
|
async def test_post(client: TestClient, package_ahriman: Package) -> None:
|
||||||
"""
|
"""
|
||||||
must create logs record
|
must create logs record
|
||||||
"""
|
"""
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||||
|
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||||
|
|
||||||
post_response = await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
post_response = await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
||||||
json={"created": 0.001, "message": "message", "process_id": 42})
|
json={"created": 0.001, "message": "message", "process_id": 42})
|
||||||
assert post_response.status == 204
|
assert post_response.status == 204
|
||||||
|
|
||||||
response = await client.get(f"/api/v1/packages/{package_ahriman.base}/logs")
|
response = await client.get(f"/api/v1/packages/{package_ahriman.base}/logs")
|
||||||
logs = await response.json()
|
logs = await response.json()
|
||||||
assert logs == {"package_base": package_ahriman.base, "logs": "message"}
|
assert logs["logs"] == "message"
|
||||||
|
|
||||||
|
|
||||||
async def test_post_exception(client: TestClient, package_ahriman: Package) -> None:
|
async def test_post_exception(client: TestClient, package_ahriman: Package) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user