Jobs setup

This commit is contained in:
2023-06-12 21:14:07 -04:00
parent 03c1dac2f2
commit 88f91c4bd5
60 changed files with 965 additions and 495 deletions

View File

@ -0,0 +1,14 @@
<?php
namespace ProVM\Common\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Views\Blade as View;
class Jobs
{
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, View $view): ResponseInterface
{
return $view->render($response, 'jobs.list');
}
}

View File

@ -29,7 +29,7 @@ class Logging
{
$response = $handler->handle($request);
$output = [
'uri' => var_export($request->getUri(), true),
'uri' => print_r($request->getUri(), true),
'body' => $request->getParsedBody()
];
$this->getLogger()->info(\Safe\json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

View File

@ -3,6 +3,9 @@ server {
root /app/ui/public;
index index.php index.html index.htm;
access_log /var/logs/nginx/ui.access.log;
error_log /var/logs/nginx/ui.error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
@ -16,4 +19,4 @@ server {
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
}

View File

@ -8,11 +8,5 @@ Monolog\ErrorHandler::register($app->getContainer()->get(Psr\Log\LoggerInterface
try {
$app->run();
} catch (Error | Exception $e) {
$logger = $app->getContainer()->get(Psr\Log\LoggerInterface::class);
if (isset($_REQUEST)) {
$logger->debug(Safe\json_encode(compact('_REQUEST')));
}
$logger->debug(Safe\json_encode(compact('_SERVER')));
$logger->error($e);
throw $e;
$app->getContainer()->get(Psr\Log\LoggerInterface::class)->error($e);
}

View File

@ -0,0 +1,4 @@
<?php
use ProVM\Common\Controller\Jobs;
$app->get('/jobs', Jobs::class);

View File

@ -5,7 +5,12 @@
<div id="messages" class="ui basic segment"></div>
@endsection
@push('page_styles')
<link rel="stylesheet" href="//cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" />
@endpush
@push('page_scripts')
<script type="text/javascript" src="//cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script type="text/javascript">
class Message
{
@ -22,7 +27,8 @@
}
attachments
constructor({id, uid, subject, date_time, from, states, attachments}) {
constructor({id, uid, subject, date_time, from, states, attachments})
{
this.set().id(id)
.set().uid(uid)
.set().subject(subject)
@ -31,7 +37,8 @@
.set().states(states)
.set().attachments(attachments)
}
get() {
get()
{
return {
id: () => {
return this.id
@ -56,7 +63,8 @@
}
}
}
set() {
set()
{
return {
id: id => {
this.id = id
@ -99,7 +107,8 @@
}
}
}
has() {
has()
{
return {
attachments: () => {
return this.states.attachments
@ -115,7 +124,8 @@
}
}
}
doesHave() {
doesHave()
{
return {
attachments: () => {
this.states.attachments = true
@ -136,7 +146,8 @@
}
}
draw() {
draw()
{
return {
row: () => {
const format = Intl.DateTimeFormat('es-CL', {dateStyle: 'full', timeStyle: 'short'})
@ -196,14 +207,15 @@
})
},
scheduledButton: () => {
return $('<i></i>').addClass('ui green circular inverted check icon')
return $('<i></i>').addClass('ui green circle check icon')
},
schedulingButton: () => {
return $('<i></i>').addClass('ui circular inverted redo loading icon')
return $('<i></i>').addClass('ui circle redo loading icon')
}
}
}
download() {
download()
{
return {
attachments: event => {
const td = $(event.currentTarget).parent()
@ -218,7 +230,11 @@
return Send.put({
uri,
data
}).then(response => {
}).then((response, status, jqXHR) => {
if (parseInt(jqXHR.status/100) !== 2 || jqXHR.status === 204) {
td.html('')
return
}
if (response.scheduled > 0) {
td.html('').append(this.draw().scheduledButton())
}
@ -265,6 +281,7 @@
$(this.id.results).html('').append(
this.draw().table()
)
let table = new DataTable('#messages_table')
} else {
$(this.id.results).html('').append(
this.draw().empty()
@ -290,7 +307,7 @@
)
},
table: () => {
return $('<table></table>').addClass('ui table').append(
return $('<table></table>').attr('id', 'messages_table').addClass('ui table').append(
this.draw().head()
).append(
this.draw().body()
@ -325,7 +342,7 @@
).append(
$('<th></th>').html('Downloaded Attachments')
).append(
$('<th></th>')
$('<th></th>').html('Schedule?')
)
)
},
@ -334,7 +351,7 @@
this.visible.forEach((m, i) => {
const row = m.draw().row()
row.prepend(
$('<td></td>').html(i + this.current + 1)
$('<td></td>').html(i + 1)
)
tbody.append(row)
})

View File

@ -50,8 +50,12 @@
}
const list = $('<div></div>').addClass('ui list')
this.mailboxes.forEach(mb => {
let count = ''
if (typeof mb.last_checked !== 'undefined') {
count = ' (' + mb.last_checked.count + ')'
}
list.append(
$('<a></a>').addClass('item').attr('href', '{{$urls->base}}/emails/mailbox/' + mb.id).html(mb.name)
$('<a></a>').addClass('item').attr('href', '{{$urls->base}}/emails/mailbox/' + mb.id).html(mb.name + count)
)
})
parent.append(list)

View File

@ -0,0 +1,14 @@
@extends('layout.base')
@section('page_title')
Jobs
@hasSection('jobs_title')
-
@yield('jobs_title')
@endif
@endsection
@section('page_content')
<h1>Jobs</h1>
@yield('jobs_content')
@endsection

View File

@ -0,0 +1,165 @@
@extends('jobs.base')
@section('jobs_content')
<div id="jobs" class="ui basic segment"></div>
@endsection
@push('page_styles')
<link rel="stylesheet" href="//cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" />
@endpush
@push('page_scripts')
<script type="text/javascript" src="//cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script type="text/javascript">
class Job
{
id
command
arguments
states
constructor(props)
{
this.id = props.id
this.command = props.command
this.arguments = props.arguments
this.states = props.states
}
lastState()
{
return this.states.sort((a, b) => {
const d1 = new Date(a.date.date)
const d2 = new Date(b.date.date)
return d1 - d2
}).at(-1)
}
isPending()
{
return this.lastState().status === 1
}
draw()
{
return {
row: () => {
const status = this.isPending() ? 'yellow clock' : 'green check'
const formatter = new Intl.DateTimeFormat('es-CL', {dateStyle: 'full', timeStyle: 'long'})
const d = new Date(this.lastState().date.date)
return $('<tr></tr>').attr('data-id', this.id).append(
$('<td></td>').html(this.id)
).append(
$('<td></td>').html(this.command)
).append(
$('<td></td>').html(this.arguments)
).append(
$('<td></td>').append(
$('<i></i>').addClass('ui icon ' + status)
)
).append(
$('<td></td>').html(formatter.format(d))
)
}
}
}
execute()
{
const uri = '/job/' + this.id + '/execute'
Send.get(uri).then((response, status, jqXHR) => {
console.debug(jqXHR.status)
console.debug(response)
})
}
}
const jobs = {
id: '',
data: [],
get: function() {
return {
jobs: () => {
this.draw().loader()
const uri = '/jobs'
return Send.get(uri).then((response, status, jqXHR) => {
if (parseInt(jqXHR.status/100) !== 2 || jqXHR.status === 204) {
$(this.id).html('').append(
this.draw().empty()
)
return
}
if (response.jobs.length === 0) {
$(this.id).html('').append(
this.draw().empty()
)
return
}
this.data = response.jobs.map(job => {
return new Job(job)
})
this.draw().jobs()
})
}
}
},
draw: function() {
return {
loader: () => {
$(this.id).html('').append(
$('<div></div>').addClass('ui segment').append(
$('<p></p>').html('&nbsp;')
).append(
$('<div></div>').addClass('ui active dimmer').append(
$('<div></div>').addClass('ui indeterminate elastic loader')
)
).append(
$('<p></p>').html('&nbsp;')
)
)
},
empty: () => {
return $('<div></div>').addClass('ui message').html('No messages found.')
},
jobs: () => {
$(this.id).html('')
const table = $('<table></table>').addClass('ui table').attr('id', 'jobs_table')
table.append(
this.draw().head()
).append(
this.draw().body()
)
$(this.id).append(table)
const t = new DataTable('#jobs_table', {
order: [[4, 'desc']]
})
},
head: () => {
return $('<thead></thead>').append(
$('<tr></tr>').append(
$('<th></th>').html('ID')
).append(
$('<th></th>').html('Command')
).append(
$('<th></th>').html('Arguments')
).append(
$('<th></th>').html('Status')
).append(
$('<th></th>').html('Date')
)
)
},
body: () => {
const body = $('<tbody></tbody>')
this.data.forEach(job => {
body.append(job.draw().row())
})
return body
}
}
},
setup: function() {
this.get().jobs()
}
}
$().ready(() => {
jobs.id = '#jobs'
jobs.setup()
})
</script>
@endpush

View File

@ -1,5 +1,6 @@
<nav class="ui menu">
<a class="item" href="{{$urls->base}}">Inicio</a>
<a class="item" href="{{$urls->base}}/emails/mailboxes">Register Mailboxes</a>
<a class="item" href="{{$urls->base}}/jobs">Jobs</a>
</nav>
<br />

View File

@ -2,8 +2,13 @@
use Psr\Container\ContainerInterface;
return [
Monolog\Handler\DeduplicationHandler::class => function(ContainerInterface $container) {
return new Monolog\Handler\DeduplicationHandler($container->get(Monolog\Handler\RotatingFileHandler::class));
'log_processors' => function(ContainerInterface $container) {
return [
$container->get(Monolog\Processor\PsrLogMessageProcessor::class),
$container->get(Monolog\Processor\WebProcessor::class),
$container->get(Monolog\Processor\IntrospectionProcessor::class),
$container->get(Monolog\Processor\MemoryPeakUsageProcessor::class)
];
},
Monolog\Handler\RotatingFileHandler::class => function(ContainerInterface $container) {
$handler = new Monolog\Handler\RotatingFileHandler($container->get('log_file'));
@ -11,22 +16,23 @@ return [
return $handler;
},
'request_logger' => function(ContainerInterface $container) {
$logger = new Monolog\Logger('request_logger');
$handler = new Monolog\Handler\RotatingFileHandler(implode(DIRECTORY_SEPARATOR, [$container->get('folders')->logs, 'requests.log']));
$handler->setFormatter($container->get(Monolog\Formatter\SyslogFormatter::class));
$dedupHandler = new Monolog\Handler\DeduplicationHandler($handler, null, Monolog\Level::Info);
$logger->pushHandler($dedupHandler);
$logger->pushProcessor($container->get(Monolog\Processor\PsrLogMessageProcessor::class));
$logger->pushProcessor($container->get(Monolog\Processor\IntrospectionProcessor::class));
$logger->pushProcessor($container->get(Monolog\Processor\MemoryUsageProcessor::class));
return $logger;
return new Monolog\Logger('request_logger', [
(new Monolog\Handler\RotatingFileHandler(implode(DIRECTORY_SEPARATOR, [$container->get('folders')->logs, 'requests.log']))),
], $container->get('log_processors'));
},
Psr\Log\LoggerInterface::class => function(ContainerInterface $container) {
$logger = new Monolog\Logger('file_logger');
$logger->pushHandler($container->get(Monolog\Handler\DeduplicationHandler::class));
$logger->pushProcessor($container->get(Monolog\Processor\PsrLogMessageProcessor::class));
$logger->pushProcessor($container->get(Monolog\Processor\IntrospectionProcessor::class));
$logger->pushProcessor($container->get(Monolog\Processor\MemoryUsageProcessor::class));
return $logger;
return new Monolog\Logger('file', [
new Monolog\Handler\FilterHandler(
(new Monolog\Handler\RotatingFileHandler($container->get('log_file')))
->setFormatter(new Monolog\Formatter\LineFormatter(null, null, true)),
Monolog\Level::Error
),
new Monolog\Handler\FilterHandler(
(new Monolog\Handler\RotatingFileHandler(implode(DIRECTORY_SEPARATOR, [$container->get('folders')->logs, 'debug.log'])))
->setFormatter(new Monolog\Formatter\LineFormatter(null, null, true)),
Monolog\Level::Debug,
Monolog\Level::Warning
)
], $container->get('log_processors'));
},
];