mirror of
https://github.com/YunoHost/yunorunner.git
synced 2024-09-03 20:05:52 +02:00
114 lines
4.3 KiB
HTML
114 lines
4.3 KiB
HTML
<% extends "base.html" %>
|
|
|
|
<% block content %>
|
|
<section class="section" id="job">
|
|
<div class="container" v-bind:class="{deleted: job.deleted}">
|
|
<h1 class="title">
|
|
<span v-if="job.deleted">(DELETED)</span>
|
|
Job #{{job.id}}
|
|
<% if app %><a href="<{ relative_path_to_root }>apps/<{ app.name }>/">{{ job.name }} </a>
|
|
<% else %>{{ job.name }}
|
|
<% endif %>
|
|
<a target="_blank" v-bind:href="job.url_or_path">↪</a>
|
|
</h1>
|
|
|
|
<div style="margin-bottom: 15px" v-if="!job.deleted">
|
|
<button class="button is-warning" v-on:click="cancelJob">Cancel job</button>
|
|
<button class="button is-default" v-on:click="restartJob">Restart job</button>
|
|
</div>
|
|
|
|
<table v-bind:class="['table', 'is-bordered', job.state + 'Job']">
|
|
<tr><th>State</th><td>{{job.state}}</td></tr>
|
|
<tr><th>Created time</th><td>{{timestampToDate(job.created_time)}}</td></tr>
|
|
<tr><th>Started time</th><td>{{timestampToDate(job.started_time)}}</td></tr>
|
|
<tr><th>End time</th><td>{{timestampToDate(job.end_time)}}</td></tr>
|
|
<tr><th>Badge</th><td><img src="<{ shield_badge_url }>" alt="<{job.state}>" title="Click to copy its Markdown code" id="badge"/></td></tr>
|
|
</table>
|
|
|
|
<h2 class="subtitle">Execution log:</h2>
|
|
<pre class="consoleOutput" v-html="logWithColors"></pre>
|
|
<div style="float:right">
|
|
<input type="checkbox" id="auto_scroll" name="auto_scroll" value="">
|
|
<label for="auto_scroll"> Auto-scroll</label>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<% endblock %>
|
|
|
|
<% block javascript %>
|
|
<script>
|
|
(function() {
|
|
var app = new Vue({
|
|
el: '#job',
|
|
data: {
|
|
job: {}
|
|
},
|
|
methods: {
|
|
timestampToDate: function (timestamp) {
|
|
if (timestamp === null) return "";
|
|
|
|
return new Date(timestamp * 1000).toLocaleString()
|
|
},
|
|
cancelJob: function() {
|
|
$.post("<{ relative_path_to_root }>api/job/" + this.job.id + "/stop")
|
|
},
|
|
restartJob: function() {
|
|
$.post("<{ relative_path_to_root }>api/job/" + this.job.id + "/restart")
|
|
}
|
|
},
|
|
computed: {
|
|
logWithColors: function() {
|
|
if (this.job.log != undefined) {
|
|
var ansiup = new AnsiUp;
|
|
return ansiup.ansi_to_html(this.job.log);
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
ws = new ReconnectingWebSocket(websocketPrefix() + '://' + document.domain + ':' + location.port + websocketRelativePath('<{ path }>') + '/job-ws/<{ job.id }>');
|
|
|
|
ws.onmessage = function (event) {
|
|
var message = JSON.parse(event.data);
|
|
var data = message.data;
|
|
var action = message.action;
|
|
|
|
if (action == "init_job" || action == "update_job") {
|
|
if ((app.job.state !== undefined) && (app.job.state != data.state))
|
|
{
|
|
notify("Job for " + data.name + " is " + data.state);
|
|
}
|
|
|
|
data.deleted = false;
|
|
app.job = data;
|
|
} else if (action == "delete_job") {
|
|
Vue.set(app.job, "deleted", true);
|
|
Vue.set(app.job, "state", "deleted");
|
|
}
|
|
|
|
if ($("#auto_scroll")[0].checked === true)
|
|
{
|
|
var elem = $(".consoleOutput")[0];
|
|
elem.scrollTop = elem.scrollHeight;
|
|
}
|
|
};
|
|
})()
|
|
</script>
|
|
<script>
|
|
// Thanks to https://tech-wiki.online/fr/clipboard-api.html
|
|
document.querySelector('#badge').addEventListener('click', async event => {
|
|
if (!navigator.clipboard) {
|
|
// Clipboard API not available
|
|
return
|
|
}
|
|
const text = "[](<{ job_url }>)\n[](<{ job_url }>)"
|
|
try {
|
|
await navigator.clipboard.writeText(text)
|
|
} catch (err) {
|
|
console.error('Failed to copy Markdown code for the job badge', err)
|
|
}
|
|
})
|
|
</script>
|
|
<% endblock %>
|