composer update

This commit is contained in:
2020-05-10 09:29:56 +00:00
parent c6f807ebad
commit 8e93eececf
919 changed files with 11790 additions and 7005 deletions

View File

@@ -107,7 +107,7 @@ class EloquentUserProvider implements UserProvider
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
Str::contains($this->firstCredentialKey($credentials), 'password'))) {
return;
}
@@ -131,6 +131,19 @@ class EloquentUserProvider implements UserProvider
return $query->first();
}
/**
* Get the first key from the credential array.
*
* @param array $credentials
* @return string|null
*/
protected function firstCredentialKey(array $credentials)
{
foreach ($credentials as $key => $value) {
return $key;
}
}
/**
* Validate a user against the given credentials.
*

View File

@@ -1180,6 +1180,17 @@ class Blueprint
return $this->addColumn('multipolygon', $column);
}
/**
* Create a new multipolygon column on the table.
*
* @param string $column
* @return \Illuminate\Database\Schema\ColumnDefinition
*/
public function multiPolygonZ($column)
{
return $this->addColumn('multipolygonz', $column);
}
/**
* Create a new generated, computed column on the table.
*

View File

@@ -868,6 +868,17 @@ class PostgresGrammar extends Grammar
return $this->formatPostGisType('multipolygon');
}
/**
* Create the column definition for a spatial MultiPolygonZ type.
*
* @param \Illuminate\Support\Fluent $column
* @return string
*/
protected function typeMultiPolygonZ(Fluent $column)
{
return $this->formatPostGisType('multipolygonz');
}
/**
* Format the column definition for a PostGIS spatial type.
*

View File

@@ -29,7 +29,7 @@ class Application extends Container implements ApplicationContract, HttpKernelIn
*
* @var string
*/
const VERSION = '5.8.35';
const VERSION = '5.8.38';
/**
* The base path for the Laravel installation.

View File

@@ -113,7 +113,9 @@ class PackageManifest
$packages = [];
if ($this->files->exists($path = $this->vendorPath.'/composer/installed.json')) {
$packages = json_decode($this->files->get($path), true);
$installed = json_decode($this->files->get($path), true);
$packages = $installed['packages'] ?? $installed;
}
$ignoreAll = in_array('*', $ignore = $this->packagesToIgnore());

View File

@@ -38,7 +38,7 @@ class SeeInOrder extends Constraint
* @param array $values
* @return bool
*/
public function matches($values) : bool
public function matches($values): bool
{
$position = 0;
@@ -67,7 +67,7 @@ class SeeInOrder extends Constraint
* @param array $values
* @return string
*/
public function failureDescription($values) : string
public function failureDescription($values): string
{
return sprintf(
'Failed asserting that \'%s\' contains "%s" in specified order.',
@@ -81,7 +81,7 @@ class SeeInOrder extends Constraint
*
* @return string
*/
public function toString() : string
public function toString(): string
{
return (new ReflectionClass($this))->name;
}

View File

@@ -94,7 +94,7 @@ abstract class ServiceProvider
*/
protected function loadViewsFrom($path, $namespace)
{
if (is_array($this->app->config['view']['paths'])) {
if (isset($this->app->config['view']['paths']) && is_array($this->app->config['view']['paths'])) {
foreach ($this->app->config['view']['paths'] as $viewPath) {
if (is_dir($appPath = $viewPath.'/vendor/'.$namespace)) {
$this->app['view']->addNamespace($namespace, $appPath);

View File

@@ -1630,7 +1630,7 @@ trait ValidatesAttributes
((aaa|aaas|about|acap|acct|acr|adiumxtra|afp|afs|aim|apt|attachment|aw|barion|beshare|bitcoin|blob|bolo|callto|cap|chrome|chrome-extension|cid|coap|coaps|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-playcontainer|dlna-playsingle|dns|dntp|dtn|dvb|ed2k|example|facetime|fax|feed|feedready|file|filesystem|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|ham|hcp|http|https|iax|icap|icon|im|imap|info|iotdisco|ipn|ipp|ipps|irc|irc6|ircs|iris|iris.beep|iris.lwz|iris.xpc|iris.xpcs|itms|jabber|jar|jms|keyparc|lastfm|ldap|ldaps|magnet|mailserver|mailto|maps|market|message|mid|mms|modem|ms-help|ms-settings|ms-settings-airplanemode|ms-settings-bluetooth|ms-settings-camera|ms-settings-cellular|ms-settings-cloudstorage|ms-settings-emailandaccounts|ms-settings-language|ms-settings-location|ms-settings-lock|ms-settings-nfctransactions|ms-settings-notifications|ms-settings-power|ms-settings-privacy|ms-settings-proximity|ms-settings-screenrotation|ms-settings-wifi|ms-settings-workplace|msnim|msrp|msrps|mtqp|mumble|mupdate|mvn|news|nfs|ni|nih|nntp|notes|oid|opaquelocktoken|pack|palm|paparazzi|pkcs11|platform|pop|pres|prospero|proxy|psyc|query|redis|rediss|reload|res|resource|rmi|rsync|rtmfp|rtmp|rtsp|rtsps|rtspu|secondlife|s3|service|session|sftp|sgn|shttp|sieve|sip|sips|skype|smb|sms|smtp|snews|snmp|soap.beep|soap.beeps|soldat|spotify|ssh|steam|stun|stuns|submit|svn|tag|teamspeak|tel|teliaeid|telnet|tftp|things|thismessage|tip|tn3270|turn|turns|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|videotex|view-source|wais|webcal|ws|wss|wtai|wyciwyg|xcon|xcon-userid|xfire|xmlrpc\.beep|xmlrpc.beeps|xmpp|xri|ymsgr|z39\.50|z39\.50r|z39\.50s)):// # protocol
(([\pL\pN-]+:)?([\pL\pN-]+)@)? # basic auth
(
([\pL\pN\pS\-\.])+(\.?([\pL]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
([\pL\pN\pS\-\_\.])+(\.?([\pL]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
| # or
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address
| # or

View File

@@ -1,7 +1,7 @@
<p align="center"><img src="https://laravel.com/assets/img/components/logo-horizon.svg"></p>
<p align="center">
<a href="https://travis-ci.org/laravel/horizon"><img src="https://travis-ci.org/laravel/horizon.svg" alt="Build Status"></a>
<a href="https://github.com/laravel/horizon/actions"><img src="https://github.com/laravel/horizon/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/horizon"><img src="https://poser.pugx.org/laravel/horizon/d/total.svg" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/horizon"><img src="https://poser.pugx.org/laravel/horizon/v/stable.svg" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/horizon"><img src="https://poser.pugx.org/laravel/horizon/license.svg" alt="License"></a>

View File

@@ -95,6 +95,7 @@ return [
'trim' => [
'recent' => 60,
'completed' => 60,
'recent_failed' => 10080,
'failed' => 10080,
'monitored' => 10080,

View File

@@ -1100,15 +1100,9 @@
}
},
"acorn": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
"integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
"dev": true
},
"acorn-dynamic-import": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
"integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
"integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==",
"dev": true
},
"adjust-sourcemap-loader": {
@@ -1564,9 +1558,9 @@
}
},
"base64-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
"dev": true
},
"batch": {
@@ -1785,9 +1779,9 @@
}
},
"buffer": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
"dev": true,
"requires": {
"base64-js": "^1.0.2",
@@ -1826,25 +1820,34 @@
"dev": true
},
"cacache": {
"version": "11.3.2",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz",
"integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==",
"version": "12.0.3",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
"integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==",
"dev": true,
"requires": {
"bluebird": "^3.5.3",
"bluebird": "^3.5.5",
"chownr": "^1.1.1",
"figgy-pudding": "^3.5.1",
"glob": "^7.1.3",
"glob": "^7.1.4",
"graceful-fs": "^4.1.15",
"infer-owner": "^1.0.3",
"lru-cache": "^5.1.1",
"mississippi": "^3.0.0",
"mkdirp": "^0.5.1",
"move-concurrently": "^1.0.1",
"promise-inflight": "^1.0.1",
"rimraf": "^2.6.2",
"rimraf": "^2.6.3",
"ssri": "^6.0.1",
"unique-filename": "^1.1.1",
"y18n": "^4.0.0"
},
"dependencies": {
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true
}
}
},
"cache-base": {
@@ -1996,15 +1999,15 @@
}
},
"chownr": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
"integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==",
"dev": true
},
"chrome-trace-event": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz",
"integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
"integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
@@ -2252,13 +2255,10 @@
"dev": true
},
"console-browserify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
"integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
"dev": true,
"requires": {
"date-now": "^0.1.4"
}
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
"integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
"dev": true
},
"consolidate": {
"version": "0.15.1",
@@ -2730,15 +2730,9 @@
}
},
"cyclist": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
"dev": true
},
"date-now": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
"integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
"dev": true
},
"de-indent": {
@@ -2891,9 +2885,9 @@
"dev": true
},
"des.js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
"integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
"integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
"dev": true,
"requires": {
"inherits": "^2.0.1",
@@ -3042,9 +3036,9 @@
"dev": true
},
"elliptic": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
"integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
@@ -3194,9 +3188,9 @@
}
},
"estraverse": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"dev": true
},
"esutils": {
@@ -3218,9 +3212,9 @@
"dev": true
},
"events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
"integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
"integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==",
"dev": true
},
"eventsource": {
@@ -4849,10 +4843,10 @@
"integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
"dev": true
},
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
"infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
"integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
"dev": true
},
"inflight": {
@@ -5942,9 +5936,9 @@
"dev": true
},
"node-libs-browser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz",
"integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
"integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
"dev": true,
"requires": {
"assert": "^1.1.1",
@@ -5957,7 +5951,7 @@
"events": "^3.0.0",
"https-browserify": "^1.0.0",
"os-browserify": "^0.3.0",
"path-browserify": "0.0.0",
"path-browserify": "0.0.1",
"process": "^0.11.10",
"punycode": "^1.2.4",
"querystring-es3": "^0.2.0",
@@ -5969,7 +5963,7 @@
"tty-browserify": "0.0.0",
"url": "^0.11.0",
"util": "^0.11.0",
"vm-browserify": "0.0.4"
"vm-browserify": "^1.0.1"
},
"dependencies": {
"punycode": {
@@ -6287,12 +6281,12 @@
"dev": true
},
"parallel-transform": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
"integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
"integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
"dev": true,
"requires": {
"cyclist": "~0.2.2",
"cyclist": "^1.0.1",
"inherits": "^2.0.3",
"readable-stream": "^2.1.5"
}
@@ -6307,9 +6301,9 @@
}
},
"parse-asn1": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
"integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
"integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
"dev": true,
"requires": {
"asn1.js": "^4.0.0",
@@ -6349,9 +6343,9 @@
"dev": true
},
"path-browserify": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
"integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
"integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
"dev": true
},
"path-dirname": {
@@ -7637,9 +7631,9 @@
}
},
"serialize-javascript": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.7.0.tgz",
"integrity": "sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
"dev": true
},
"serve-index": {
@@ -7696,9 +7690,9 @@
"dev": true
},
"set-value": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
"integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
"integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
@@ -8211,9 +8205,9 @@
}
},
"stream-shift": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
"dev": true
},
"string-width": {
@@ -8377,22 +8371,28 @@
}
},
"terser-webpack-plugin": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.4.tgz",
"integrity": "sha512-64IiILNQlACWZLzFlpzNaG0bpQ4ytaB7fwOsbpsdIV70AfLUmIGGeuKL0YV2WmtcrURjE2aOvHD4/lrFV3Rg+Q==",
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
"integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
"dev": true,
"requires": {
"cacache": "^11.3.2",
"find-cache-dir": "^2.0.0",
"cacache": "^12.0.2",
"find-cache-dir": "^2.1.0",
"is-wsl": "^1.1.0",
"schema-utils": "^1.0.0",
"serialize-javascript": "^1.7.0",
"serialize-javascript": "^2.1.2",
"source-map": "^0.6.1",
"terser": "^3.17.0",
"webpack-sources": "^1.3.0",
"terser": "^4.1.2",
"webpack-sources": "^1.4.0",
"worker-farm": "^1.7.0"
},
"dependencies": {
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@@ -8409,6 +8409,27 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"terser": {
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz",
"integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==",
"dev": true,
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
}
},
"webpack-sources": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
"integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
"dev": true,
"requires": {
"source-list-map": "^2.0.0",
"source-map": "~0.6.1"
}
}
}
},
@@ -8435,9 +8456,9 @@
"dev": true
},
"timers-browserify": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz",
"integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==",
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
"integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==",
"dev": true,
"requires": {
"setimmediate": "^1.0.4"
@@ -8516,9 +8537,9 @@
"dev": true
},
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
"dev": true
},
"tty-browserify": {
@@ -8596,43 +8617,22 @@
"dev": true
},
"union-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
"integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
"integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
"dev": true,
"requires": {
"arr-union": "^3.1.0",
"get-value": "^2.0.6",
"is-extendable": "^0.1.1",
"set-value": "^0.4.3"
"set-value": "^2.0.1"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"requires": {
"is-extendable": "^0.1.0"
}
},
"is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
"dev": true
},
"set-value": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
"integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
"is-extendable": "^0.1.1",
"is-plain-object": "^2.0.1",
"to-object-path": "^0.3.0"
}
}
}
},
@@ -8658,9 +8658,9 @@
}
},
"unique-slug": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz",
"integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
"integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
"dev": true,
"requires": {
"imurmurhash": "^0.1.4"
@@ -8841,13 +8841,10 @@
"dev": true
},
"vm-browserify": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
"integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
"dev": true,
"requires": {
"indexof": "0.0.1"
}
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
"dev": true
},
"vue": {
"version": "2.5.22",
@@ -8933,37 +8930,60 @@
}
},
"webpack": {
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.32.0.tgz",
"integrity": "sha512-ofFq9jjAn4HRzlmkcZZrjijbRZcqDw+mM9KrjKd0r6lS0qxyZ7jzICzhphGafXL62dGdjP7TgMK9mZeMLUgZgw==",
"version": "4.41.5",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.5.tgz",
"integrity": "sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.8.5",
"@webassemblyjs/helper-module-context": "1.8.5",
"@webassemblyjs/wasm-edit": "1.8.5",
"@webassemblyjs/wasm-parser": "1.8.5",
"acorn": "^6.0.5",
"acorn-dynamic-import": "^4.0.0",
"ajv": "^6.1.0",
"ajv-keywords": "^3.1.0",
"chrome-trace-event": "^1.0.0",
"acorn": "^6.2.1",
"ajv": "^6.10.2",
"ajv-keywords": "^3.4.1",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^4.1.0",
"eslint-scope": "^4.0.0",
"eslint-scope": "^4.0.3",
"json-parse-better-errors": "^1.0.2",
"loader-runner": "^2.3.0",
"loader-utils": "^1.1.0",
"memory-fs": "~0.4.1",
"micromatch": "^3.1.8",
"mkdirp": "~0.5.0",
"neo-async": "^2.5.0",
"node-libs-browser": "^2.0.0",
"loader-runner": "^2.4.0",
"loader-utils": "^1.2.3",
"memory-fs": "^0.4.1",
"micromatch": "^3.1.10",
"mkdirp": "^0.5.1",
"neo-async": "^2.6.1",
"node-libs-browser": "^2.2.1",
"schema-utils": "^1.0.0",
"tapable": "^1.1.0",
"terser-webpack-plugin": "^1.1.0",
"watchpack": "^1.5.0",
"webpack-sources": "^1.3.0"
"tapable": "^1.1.3",
"terser-webpack-plugin": "^1.4.3",
"watchpack": "^1.6.0",
"webpack-sources": "^1.4.1"
},
"dependencies": {
"ajv": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ajv-keywords": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
"dev": true
},
"neo-async": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
"integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
"dev": true
},
"schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@@ -8974,6 +8994,22 @@
"ajv-errors": "^1.0.0",
"ajv-keywords": "^3.1.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"webpack-sources": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
"integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
"dev": true,
"requires": {
"source-list-map": "^2.0.0",
"source-map": "~0.6.1"
}
}
}
},
@@ -9256,9 +9292,9 @@
"dev": true
},
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true
},
"y18n": {
@@ -9268,9 +9304,9 @@
"dev": true
},
"yallist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
"yargs": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
{
"/app.js": "/app.js?id=dbc6b1d1957be6dae833",
"/app.css": "/app.css?id=b95d548aba488172f4b4",
"/app-dark.css": "/app-dark.css?id=b5b064c2f5a4b673a1c5"
"/app.js": "/app.js?id=d0c3a4150154ed18692d",
"/app.css": "/app.css?id=81fbc8fec874ad3203d7",
"/app-dark.css": "/app-dark.css?id=3d68663a6ab8e86cb5dc"
}

View File

@@ -1,13 +1,17 @@
import Vue from 'vue';
import Base from './base';
import _ from 'lodash';
import axios from 'axios';
import Routes from './routes';
import VueRouter from 'vue-router';
import VueJsonPretty from 'vue-json-pretty';
import moment from 'moment-timezone';
require('bootstrap');
window.Popper = require('popper.js').default;
try {
window.$ = window.jQuery = require('jquery');
require('bootstrap');
} catch (e) {}
let token = document.head.querySelector('meta[name="csrf-token"]');
@@ -19,14 +23,21 @@ if (token) {
Vue.use(VueRouter);
window.Popper = require('popper.js').default;
Vue.prototype.$http = axios.create();
window.Horizon.basePath = '/' + window.Horizon.path;
let routerBasePath = window.Horizon.basePath + '/';
if (window.Horizon.path === '' || window.Horizon.path === '/') {
routerBasePath = '/';
window.Horizon.basePath = '';
}
const router = new VueRouter({
routes: Routes,
mode: 'history',
base: '/' + window.Horizon.path + '/',
base: routerBasePath,
});
Vue.component('vue-json-pretty', VueJsonPretty);
@@ -34,6 +45,14 @@ Vue.component('alert', require('./components/Alert.vue').default);
Vue.mixin(Base);
Vue.directive('tooltip', function(el, binding) {
$(el).tooltip({
title: binding.value,
placement: binding.arg,
trigger: 'hover',
});
});
new Vue({
el: '#horizon',

View File

@@ -1,4 +1,3 @@
import _ from 'lodash';
import moment from 'moment-timezone';
export default {
@@ -9,43 +8,6 @@ export default {
},
methods: {
/**
* Show the time ago format for the given time.
*/
timeAgo(time) {
moment.updateLocale('en', {
relativeTime: {
future: 'in %s',
past: '%s ago',
s: number => number + 's ago',
ss: '%ds ago',
m: '1m ago',
mm: '%dm ago',
h: '1h ago',
hh: '%dh ago',
d: '1d ago',
dd: '%dd ago',
M: 'a month ago',
MM: '%d months ago',
y: 'a year ago',
yy: '%d years ago',
},
});
let secondsElapsed = moment().diff(time, 'seconds');
let dayStart = moment('2018-01-01')
.startOf('day')
.seconds(secondsElapsed);
if (secondsElapsed > 300) {
return moment(time).fromNow(true);
} else if (secondsElapsed < 60) {
return dayStart.format('s') + 's ago';
} else {
return dayStart.format('m:ss') + 'm ago';
}
},
/**
* Format the given date with respect to timezone.
*/
@@ -83,74 +45,5 @@ export default {
readableTimestamp(timestamp) {
return this.formatDate(timestamp).format('YYYY-MM-DD HH:mm:ss');
},
/**
* Format the tags.
*/
displayableTagsList(tags, truncate = true) {
if (!tags || !tags.length) return '';
return _.reduce(
tags,
(s, n) => {
return (s ? s + ', ' : '') + (truncate ? _.truncate(n) : n);
},
''
);
},
/**
* Show the time in local time.
*/
localTime(time) {
return moment(time + ' Z')
.utc()
.local()
.format('MMMM Do YYYY, h:mm:ss A');
},
/**
* Truncate the given string.
*/
truncate(string, length = 70) {
return _.truncate(string, {
length: length,
separator: /,? +/,
});
},
/**
* Creates a debounced function that delays invoking a callback.
*/
debouncer: _.debounce(callback => callback(), 500),
/**
* Show an error message.
*/
alertError(message) {
this.$root.alert.type = 'error';
this.$root.alert.autoClose = false;
this.$root.alert.message = message;
},
/**
* Show a success message.
*/
alertSuccess(message, autoClose) {
this.$root.alert.type = 'success';
this.$root.alert.autoClose = autoClose;
this.$root.alert.message = message;
},
/**
* Show confirmation message.
*/
alertConfirm(message, success, failure) {
this.$root.alert.type = 'confirmation';
this.$root.alert.autoClose = false;
this.$root.alert.message = message;
this.$root.alert.confirmationProceed = success;
this.$root.alert.confirmationCancel = failure;
},
},
};

View File

@@ -1,6 +1,4 @@
<script type="text/ecmascript-6">
import $ from 'jquery';
export default {
props: ['type', 'message', 'autoClose', 'confirmationProceed', 'confirmationCancel'],

View File

@@ -1,5 +1,5 @@
<script type="text/ecmascript-6">
import _ from "lodash"
import _take from "lodash/take"
export default {
props: ['trace'],
@@ -16,7 +16,7 @@
computed: {
lines(){
return this.showAll ? _.take(this.trace, 1000) : _.take(this.trace, this.minimumLines);
return this.showAll ? _take(this.trace, 1000) : _take(this.trace, this.minimumLines);
}
}
}
@@ -38,4 +38,4 @@
<style scoped>
</style>
</style>

View File

@@ -63,6 +63,12 @@ export default [
component: require('./screens/recentJobs/index').default,
},
{
path: '/recent-jobs/:jobId',
name: 'recent-jobs-preview',
component: require('./screens/recentJobs/job').default,
},
{
path: '/failed',
name: 'failed-jobs',

View File

@@ -64,7 +64,7 @@
* Load the general stats.
*/
loadStats() {
return this.$http.get('/' + Horizon.path + '/api/stats')
return this.$http.get(Horizon.basePath + '/api/stats')
.then(response => {
this.stats = response.data;
@@ -80,7 +80,7 @@
* Load the workers stats.
*/
loadWorkers() {
return this.$http.get('/' + Horizon.path + '/api/masters')
return this.$http.get(Horizon.basePath + '/api/masters')
.then(response => {
this.workers = response.data;
});
@@ -91,7 +91,7 @@
* Load the workload stats.
*/
loadWorkload() {
return this.$http.get('/' + Horizon.path + '/api/workload')
return this.$http.get(Horizon.basePath + '/api/workload')
.then(response => {
this.workload = response.data;
});

View File

@@ -70,7 +70,7 @@
var tagQuery = this.tagSearchPhrase ? 'tag=' + this.tagSearchPhrase + '&' : '';
this.$http.get('/' + Horizon.path + '/api/jobs/failed?' + tagQuery + 'starting_at=' + starting)
this.$http.get(Horizon.basePath + '/api/jobs/failed?' + tagQuery + 'starting_at=' + starting)
.then(response => {
if (!this.$root.autoLoadsNewEntries && refreshing && !response.data.jobs.length) {
return;
@@ -109,7 +109,7 @@
this.retryingJobs.push(id);
this.$http.post('/' + Horizon.path + '/api/jobs/retry/' + id)
this.$http.post(Horizon.basePath + '/api/jobs/retry/' + id)
.then((response) => {
setTimeout(() => {
this.retryingJobs = _.reject(this.retryingJobs, job => job == id);

View File

@@ -44,7 +44,7 @@
loadFailedJob(id) {
this.ready = false;
this.$http.get('/' + Horizon.path + '/api/jobs/failed/' + id)
this.$http.get(Horizon.basePath + '/api/jobs/failed/' + id)
.then(response => {
this.job = response.data;
@@ -57,7 +57,7 @@
* Reload the job retries.
*/
reloadRetries() {
this.$http.get('/' + Horizon.path + '/api/jobs/failed/' + this.$route.params.jobId)
this.$http.get(Horizon.basePath + '/api/jobs/failed/' + this.$route.params.jobId)
.then(response => {
this.job.retried_by = response.data.retried_by;
@@ -75,7 +75,7 @@
this.retrying = true;
this.$http.post('/' + Horizon.path + '/api/jobs/retry/' + id)
this.$http.post(Horizon.basePath + '/api/jobs/retry/' + id)
.then(() => {
setTimeout(() => {
this.reloadRetries();
@@ -211,7 +211,7 @@
</td>
<td class="table-fit">
<a v-if="retry.status == 'failed'" :href="'/' + Horizon.path + '/failed/'+retry.id">
<a v-if="retry.status == 'failed'" :href="Horizon.basePath + '/failed/'+retry.id">
{{ retry.id }}
</a>
<span v-else>{{ retry.id }}</span>

View File

@@ -29,7 +29,7 @@
loadJobs() {
this.ready = false;
this.$http.get('/' + Horizon.path + '/api/metrics/jobs')
this.$http.get(Horizon.basePath + '/api/metrics/jobs')
.then(response => {
this.jobs = response.data;

View File

@@ -36,7 +36,7 @@
loadMetric() {
this.ready = false;
this.$http.get('/' + Horizon.path + '/api/metrics/' + this.$route.params.type + '/' + encodeURIComponent(this.$route.params.slug))
this.$http.get(Horizon.basePath + '/api/metrics/' + this.$route.params.type + '/' + encodeURIComponent(this.$route.params.slug))
.then(response => {
let data = this.prepareData(response.data);

View File

@@ -29,7 +29,7 @@
loadQueues() {
this.ready = false;
this.$http.get('/' + Horizon.path + '/api/metrics/queues')
this.$http.get(Horizon.basePath + '/api/metrics/queues')
.then(response => {
this.queues = response.data;

View File

@@ -1,6 +1,4 @@
<script type="text/ecmascript-6">
import $ from 'jquery';
export default {
/**
* The component's data.
@@ -36,7 +34,7 @@
loadTags() {
this.ready = false;
this.$http.get('/' + Horizon.path + '/api/monitoring')
this.$http.get(Horizon.basePath + '/api/monitoring')
.then(response => {
this.tags = response.data;
@@ -66,7 +64,7 @@
return;
}
this.$http.post('/' + Horizon.path + '/api/monitoring', {'tag': this.newTag})
this.$http.post(Horizon.basePath + '/api/monitoring', {'tag': this.newTag})
.then(response => {
$('#addTagModal').modal('hide');
@@ -92,7 +90,7 @@
* Stop monitoring the given tag.
*/
stopMonitoring(tag) {
this.$http.delete('/' + Horizon.path + '/api/monitoring/' + encodeURIComponent(tag))
this.$http.delete(Horizon.basePath + '/api/monitoring/' + encodeURIComponent(tag))
.then(() => {
this.tags = _.reject(this.tags, existing => existing.tag == tag)
})

View File

@@ -61,7 +61,7 @@
tag = this.type == 'failed' ? 'failed:' + tag : tag;
this.$http.get('/' + Horizon.path + '/api/monitoring/' + encodeURIComponent(tag) + '?starting_at=' + starting + '&limit=' + this.perPage)
this.$http.get(Horizon.basePath + '/api/monitoring/' + encodeURIComponent(tag) + '?starting_at=' + starting + '&limit=' + this.perPage)
.then(response => {
if (!this.$root.autoLoadsNewEntries && refreshing && this.jobs.length && _.first(response.data.jobs).id !== _.first(this.jobs).id) {
this.hasNewEntries = true;

View File

@@ -1,5 +1,5 @@
<script type="text/ecmascript-6">
import $ from 'jquery';
import JobRow from './job-row';
export default {
/**
@@ -7,8 +7,6 @@
*/
data() {
return {
tagSearchPhrase: '',
searchTimeout: null,
ready: false,
loadingNewEntries: false,
hasNewEntries: false,
@@ -19,6 +17,13 @@
};
},
/**
* Components
*/
components: {
JobRow,
},
/**
* Prepare the component.
*/
@@ -46,16 +51,6 @@
this.page = 1;
this.loadJobs(this.$route.params.tag);
},
tagSearchPhrase() {
clearTimeout(this.searchTimeout);
clearInterval(this.interval);
this.searchTimeout = setTimeout(() => {
this.loadJobs();
this.refreshJobsPeriodically();
}, 500);
}
},
@@ -69,9 +64,7 @@
this.ready = false;
}
var tagQuery = this.tagSearchPhrase ? 'tag=' + this.tagSearchPhrase + '&' : '';
this.$http.get('/' + Horizon.path + '/api/jobs/recent?' + tagQuery + 'starting_at=' + starting + '&limit=' + this.perPage)
this.$http.get(Horizon.basePath + '/api/jobs/recent?starting_at=' + starting + '&limit=' + this.perPage)
.then(response => {
if (!this.$root.autoLoadsNewEntries && refreshing && this.jobs.length && _.first(response.data.jobs).id !== _.first(this.jobs).id) {
this.hasNewEntries = true;
@@ -144,20 +137,21 @@
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between">
<h5>Recent Jobs</h5>
<input type="text" class="form-control" v-model="tagSearchPhrase" placeholder="Search Tags" style="width:200px">
</div>
<div v-if="!ready" class="d-flex align-items-center justify-content-center card-bg-secondary p-5 bottom-radius">
<div v-if="!ready"
class="d-flex align-items-center justify-content-center card-bg-secondary p-5 bottom-radius">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="icon spin mr-2 fill-text-color">
<path d="M12 10a2 2 0 0 1-3.41 1.41A2 2 0 0 1 10 8V0a9.97 9.97 0 0 1 10 10h-8zm7.9 1.41A10 10 0 1 1 8.59.1v2.03a8 8 0 1 0 9.29 9.29h2.02zm-4.07 0a6 6 0 1 1-7.25-7.25v2.1a3.99 3.99 0 0 0-1.4 6.57 4 4 0 0 0 6.56-1.42h2.1z"></path>
<path
d="M12 10a2 2 0 0 1-3.41 1.41A2 2 0 0 1 10 8V0a9.97 9.97 0 0 1 10 10h-8zm7.9 1.41A10 10 0 1 1 8.59.1v2.03a8 8 0 1 0 9.29 9.29h2.02zm-4.07 0a6 6 0 1 1-7.25-7.25v2.1a3.99 3.99 0 0 0-1.4 6.57 4 4 0 0 0 6.56-1.42h2.1z"></path>
</svg>
<span>Loading...</span>
</div>
<div v-if="ready && jobs.length == 0" class="d-flex flex-column align-items-center justify-content-center card-bg-secondary p-5 bottom-radius">
<div v-if="ready && jobs.length == 0"
class="d-flex flex-column align-items-center justify-content-center card-bg-secondary p-5 bottom-radius">
<span>There aren't any jobs.</span>
</div>
@@ -172,48 +166,17 @@
</thead>
<tbody>
<tr v-if="hasNewEntries" key="newEntries" class="dontanimate">
<td colspan="100" class="text-center card-bg-secondary py-1">
<small><a href="#" v-on:click.prevent="loadNewEntries" v-if="!loadingNewEntries">Load New Entries</a></small>
<tr v-if="hasNewEntries" key="newEntries" class="dontanimate">
<td colspan="100" class="text-center card-bg-secondary py-1">
<small><a href="#" v-on:click.prevent="loadNewEntries" v-if="!loadingNewEntries">Load New
Entries</a></small>
<small v-if="loadingNewEntries">Loading...</small>
</td>
</tr>
<small v-if="loadingNewEntries">Loading...</small>
</td>
</tr>
<tr v-for="job in jobs" :key="job.id">
<td>
<span v-if="job.status != 'failed'" :title="job.name">{{jobBaseName(job.name)}}</span>
<router-link v-if="job.status === 'failed'" :title="job.name" :to="{ name: 'failed-jobs-preview', params: { jobId: job.id }}">
{{ jobBaseName(job.name) }}
</router-link><br>
<small class="text-muted">
Queue: {{job.queue}}
<span v-if="job.payload.tags.length">| Tags: {{ job.payload.tags && job.payload.tags.length ? job.payload.tags.join(', ') : '' }}</span>
</small>
</td>
<td class="table-fit">
{{ readableTimestamp(job.payload.pushedAt) }}
</td>
<td class="table-fit">
<span>{{ job.completed_at ? (job.completed_at - job.reserved_at).toFixed(2)+'s' : '-' }}</span>
</td>
<td class="text-right table-fit">
<svg v-if="job.status == 'completed'" class="fill-success" viewBox="0 0 20 20" style="width: 1.5rem; height: 1.5rem;">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM6.7 9.29L9 11.6l4.3-4.3 1.4 1.42L9 14.4l-3.7-3.7 1.4-1.42z"></path>
</svg>
<svg v-if="job.status == 'reserved' || job.status == 'pending'" class="fill-warning" viewBox="0 0 20 20" style="width: 1.5rem; height: 1.5rem;">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM7 6h2v8H7V6zm4 0h2v8h-2V6z"/>
</svg>
<svg v-if="job.status == 'failed'" class="fill-danger" viewBox="0 0 20 20" style="width: 1.5rem; height: 1.5rem;">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm1.41-1.41A8 8 0 1 0 15.66 4.34 8 8 0 0 0 4.34 15.66zm9.9-8.49L11.41 10l2.83 2.83-1.41 1.41L10 11.41l-2.83 2.83-1.41-1.41L8.59 10 5.76 7.17l1.41-1.41L10 8.59l2.83-2.83 1.41 1.41z"/>
</svg>
</td>
</tr>
<tr v-for="job in jobs" :key="job.id" :job="job" is="job-row">
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,79 @@
<template>
<tr>
<td>
<span v-if="job.status != 'failed'" :title="job.name">{{jobBaseName(job.name)}}</span>
<router-link v-if="job.status === 'failed'" :title="job.name" :to="{ name: 'failed-jobs-preview', params: { jobId: job.id }}">
{{ jobBaseName(job.name) }}
</router-link>
<small class="badge badge-secondary badge-sm"
v-tooltip:top="`Delayed for ${delayed}`"
v-if="delayed && (job.status == 'reserved' || job.status == 'pending')">
Delayed
</small>
<br>
<small class="text-muted">
<router-link :to="{name: 'recent-jobs-preview', params: {jobId: job.id}}">View detail</router-link> |
Queue: {{job.queue}}
<span v-if="job.payload.tags.length">
| Tags: {{ job.payload.tags && job.payload.tags.length ? job.payload.tags.slice(0,3).join(', ') : '' }}<span v-if="job.payload.tags.length > 3"> ({{ job.payload.tags.length - 3 }} more)</span>
</span>
</small>
</td>
<td class="table-fit">
{{ readableTimestamp(job.payload.pushedAt) }}
</td>
<td class="table-fit">
<span>{{ job.completed_at ? (job.completed_at - job.reserved_at).toFixed(2)+'s' : '-' }}</span>
</td>
<td class="text-right table-fit">
<svg v-if="job.status == 'completed'" class="fill-success" viewBox="0 0 20 20" style="width: 1.5rem; height: 1.5rem;">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM6.7 9.29L9 11.6l4.3-4.3 1.4 1.42L9 14.4l-3.7-3.7 1.4-1.42z"></path>
</svg>
<svg v-if="job.status == 'reserved' || job.status == 'pending'" class="fill-warning" viewBox="0 0 20 20" style="width: 1.5rem; height: 1.5rem;">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM7 6h2v8H7V6zm4 0h2v8h-2V6z"/>
</svg>
<svg v-if="job.status == 'failed'" class="fill-danger" viewBox="0 0 20 20" style="width: 1.5rem; height: 1.5rem;">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm1.41-1.41A8 8 0 1 0 15.66 4.34 8 8 0 0 0 4.34 15.66zm9.9-8.49L11.41 10l2.83 2.83-1.41 1.41L10 11.41l-2.83 2.83-1.41-1.41L8.59 10 5.76 7.17l1.41-1.41L10 8.59l2.83-2.83 1.41 1.41z"/>
</svg>
</td>
</tr>
</template>
<script type="text/ecmascript-6">
import phpunserialize from 'phpunserialize'
import moment from 'moment-timezone';
export default {
props: {
job: {
type: Object,
required: true
}
},
computed: {
unserialized() {
return phpunserialize(this.job.payload.data.command);
},
delayed() {
if (this.unserialized && this.unserialized.delay){
return moment.utc(this.unserialized.delay.date).fromNow(true);
}
return null;
},
},
}
</script>

View File

@@ -0,0 +1,152 @@
<template>
<div>
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between">
<h5 v-if="!ready">Job Preview</h5>
<h5 v-if="ready">{{job.name}}</h5>
<a data-toggle="collapse" href="#collapseDetails" role="button">
Collapse
</a>
</div>
<div v-if="!ready" class="d-flex align-items-center justify-content-center card-bg-secondary p-5 bottom-radius">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="icon spin mr-2 fill-text-color">
<path d="M12 10a2 2 0 0 1-3.41 1.41A2 2 0 0 1 10 8V0a9.97 9.97 0 0 1 10 10h-8zm7.9 1.41A10 10 0 1 1 8.59.1v2.03a8 8 0 1 0 9.29 9.29h2.02zm-4.07 0a6 6 0 1 1-7.25-7.25v2.1a3.99 3.99 0 0 0-1.4 6.57 4 4 0 0 0 6.56-1.42h2.1z"></path>
</svg>
<span>Loading...</span>
</div>
<div class="card-body card-bg-secondary collapse show" id="collapseDetails" v-if="ready">
<div class="row mb-2">
<div class="col-md-2"><strong>ID</strong></div>
<div class="col">{{job.id}}</div>
</div>
<div class="row mb-2">
<div class="col-md-2"><strong>Queue</strong></div>
<div class="col">{{job.queue}}</div>
</div>
<div class="row mb-2">
<div class="col-md-2"><strong>Pushed At</strong></div>
<div class="col">{{ readableTimestamp(job.payload.pushedAt) }}</div>
</div>
<div class="row mb-2" v-if="delayed">
<div class="col-md-2"><strong>Delayed Until</strong></div>
<div class="col">{{delayed}}</div>
</div>
<div class="row">
<div class="col-md-2"><strong>Completed At</strong></div>
<div class="col" v-if="job.completed_at">{{readableTimestamp(job.completed_at)}}</div>
<div class="col" else>-</div>
</div>
</div>
</div>
<div class="card mt-4" v-if="ready">
<div class="card-header d-flex align-items-center justify-content-between">
<h5>Data</h5>
<a data-toggle="collapse" href="#collapseData" role="button">
Collapse
</a>
</div>
<div class="card-body code-bg text-white collapse show" id="collapseData">
<vue-json-pretty :data="prettyPrintJob(job.payload.data)"></vue-json-pretty>
</div>
</div>
<div class="card mt-4" v-if="ready && job.payload.tags.length">
<div class="card-header d-flex align-items-center justify-content-between">
<h5>Tags</h5>
<a data-toggle="collapse" href="#collapseTags" role="button">
Collapse
</a>
</div>
<div class="card-body code-bg text-white collapse show" id="collapseTags">
<vue-json-pretty :data="job.payload.tags"></vue-json-pretty>
</div>
</div>
</div>
</template>
<script type="text/ecmascript-6">
import phpunserialize from 'phpunserialize'
import moment from "moment-timezone";
export default {
components: {
'stack-trace': require('./../../components/Stacktrace').default
},
/**
* The component's data.
*/
data() {
return {
ready: false,
job: {}
};
},
computed: {
unserialized() {
return phpunserialize(this.job.payload.data.command);
},
delayed() {
let unserialized = phpunserialize(this.job.payload.data.command);
if (unserialized && unserialized.delay) {
return moment.tz(unserialized.delay.date, unserialized.delay.timezone)
.local()
.format('YYYY-MM-DD HH:mm:ss');
}
return null;
},
},
/**
* Prepare the component.
*/
mounted() {
this.loadJob(this.$route.params.jobId);
document.title = "Horizon - Job Detail";
},
methods: {
/**
* Load a job by the given ID.
*/
loadJob(id) {
this.ready = false;
this.$http.get(Horizon.basePath + '/api/jobs/recent/' + id)
.then(response => {
this.job = response.data;
this.ready = true;
});
},
/**
* Pretty print serialized job.
*/
prettyPrintJob(data) {
return data.command && !data.command.includes('CallQueuedClosure')
? phpunserialize(data.command) : data;
}
}
}
</script>

View File

@@ -268,3 +268,7 @@ button:hover {
color: #fff;
background: $danger;
}
.badge-sm {
font-size: 0.75rem;
}

View File

@@ -28,6 +28,7 @@ Route::prefix('api')->group(function () {
// Job Routes...
Route::get('/jobs/recent', 'RecentJobsController@index')->name('horizon.recent-jobs.index');
Route::get('/jobs/recent/{id}', 'RecentJobsController@show')->name('horizon.recent-jobs.show');
Route::get('/jobs/failed', 'FailedJobsController@index')->name('horizon.failed-jobs.index');
Route::get('/jobs/failed/{id}', 'FailedJobsController@show')->name('horizon.failed-jobs.show');
Route::post('/jobs/retry/{id}', 'RetryController@store')->name('horizon.retry-jobs.show');

View File

@@ -10,6 +10,7 @@ interface HorizonCommandQueue
* @param string $name
* @param string $command
* @param array $options
* @return void
*/
public function push($name, $command, array $options = []);

View File

@@ -13,7 +13,6 @@ trait EventMap
Events\JobPushed::class => [
Listeners\StoreJob::class,
Listeners\StoreMonitoredTags::class,
Listeners\StoreTagsForRecentJob::class,
],
Events\JobReserved::class => [

View File

@@ -127,7 +127,7 @@ class HorizonServiceProvider extends ServiceProvider
__DIR__.'/../config/horizon.php', 'horizon'
);
Horizon::use(config('horizon.use'));
Horizon::use(config('horizon.use', 'default'));
}
/**

View File

@@ -27,7 +27,7 @@ class DashboardStatsController extends Controller
'status' => $this->currentStatus(),
'wait' => collect(app(WaitTimeCalculator::class)->calculate())->take(1),
'periods' => [
'failedJobs' => config('horizon.trim.recent_failed', config('horizon.trim.recent')),
'failedJobs' => config('horizon.trim.recent_failed', config('horizon.trim.failed')),
'recentJobs' => config('horizon.trim.recent'),
],
];

View File

@@ -4,7 +4,6 @@ namespace Laravel\Horizon\Http\Controllers;
use Illuminate\Http\Request;
use Laravel\Horizon\Contracts\JobRepository;
use Laravel\Horizon\Contracts\TagRepository;
class RecentJobsController extends Controller
{
@@ -15,26 +14,17 @@ class RecentJobsController extends Controller
*/
public $jobs;
/**
* The tag repository implementation.
*
* @var \Laravel\Horizon\Contracts\TagRepository
*/
public $tags;
/**
* Create a new controller instance.
*
* @param \Laravel\Horizon\Contracts\JobRepository $jobs
* @param \Laravel\Horizon\Contracts\TagRepository $tags
* @return void
*/
public function __construct(JobRepository $jobs, TagRepository $tags)
public function __construct(JobRepository $jobs)
{
parent::__construct();
$this->jobs = $jobs;
$this->tags = $tags;
}
/**
@@ -45,51 +35,29 @@ class RecentJobsController extends Controller
*/
public function index(Request $request)
{
$jobs = ! $request->query('tag')
? $this->paginate($request)
: $this->paginateByTag($request, $request->query('tag'));
$jobs = $this->jobs->getRecent($request->query('starting_at', -1))->map(function ($job) {
$job->payload = json_decode($job->payload);
$total = $request->query('tag')
? $this->tags->count('recent:'.$request->query('tag'))
: $this->jobs->countRecent();
return $job;
})->values();
return [
'jobs' => $jobs,
'total' => $total,
'total' => $this->jobs->countRecent(),
];
}
/**
* Paginate the recent jobs for the request.
* Get the details of a recent job by ID.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Support\Collection
* @param string $id
* @return array
*/
protected function paginate(Request $request)
public function show($id)
{
return $this->jobs->getRecent($request->query('starting_at', -1))->map(function ($job) {
return (array) $this->jobs->getJobs([$id])->map(function ($job) {
return $this->decode($job);
})->values();
}
/**
* Paginate the recent jobs for the request and tag.
*
* @param \Illuminate\Http\Request $request
* @param string $tag
* @return \Illuminate\Support\Collection
*/
protected function paginateByTag(Request $request, $tag)
{
$jobIds = $this->tags->paginate(
'recent:'.$tag, $request->query('starting_at', -1) + 1, 50
);
$startingAt = $request->query('starting_at', 0);
return $this->jobs->getJobs($jobIds, $startingAt)->map(function ($job) {
return $this->decode($job);
});
})->first();
}
/**

View File

@@ -2,6 +2,7 @@
namespace Laravel\Horizon\Jobs;
use Cake\Chronos\Chronos;
use Illuminate\Contracts\Queue\Factory as Queue;
use Laravel\Horizon\Contracts\JobRepository;
use Laravel\Horizon\JobId;
@@ -18,7 +19,7 @@ class RetryFailedJob
/**
* Create a new job instance.
*
* @param string $id;
* @param string $id
* @return void
*/
public function __construct($id)
@@ -55,10 +56,26 @@ class RetryFailedJob
*/
protected function preparePayload($id, $payload)
{
return json_encode(array_merge(json_decode($payload, true), [
$payload = json_decode($payload, true);
return json_encode(array_merge($payload, [
'id' => $id,
'attempts' => 0,
'retry_of' => $this->id,
'timeoutAt' => $this->prepareNewTimeout($payload),
]));
}
/**
* Prepare the timeout.
*
* @param array $payload
* @return int|null
*/
protected function prepareNewTimeout($payload)
{
return $payload['timeoutAt']
? Chronos::now()->addSeconds(ceil($payload['timeoutAt'] - $payload['pushedAt']))->getTimestamp()
: null;
}
}

View File

@@ -1,46 +0,0 @@
<?php
namespace Laravel\Horizon\Listeners;
use Laravel\Horizon\Contracts\TagRepository;
use Laravel\Horizon\Events\JobPushed;
class StoreTagsForRecentJob
{
/**
* The tag repository implementation.
*
* @var \Laravel\Horizon\Contracts\TagRepository
*/
public $tags;
/**
* Create a new listener instance.
*
* @param \Laravel\Horizon\Contracts\TagRepository $tags
* @return void
*/
public function __construct(TagRepository $tags)
{
$this->tags = $tags;
}
/**
* Handle the event.
*
* @param \Laravel\Horizon\Events\JobPushed $event
* @return void
*/
public function handle(JobPushed $event)
{
$tags = collect($event->payload->tags())->map(function ($tag) {
return 'recent:'.$tag;
})->all();
$this->tags->addTemporary(
config('horizon.trim.recent', 60),
$event->payload->id(),
$tags
);
}
}

View File

@@ -31,6 +31,7 @@ class RedisHorizonCommandQueue implements HorizonCommandQueue
* @param string $name
* @param string $command
* @param array $options
* @return void
*/
public function push($name, $command, array $options = [])
{

View File

@@ -42,6 +42,13 @@ class RedisJobRepository implements JobRepository
*/
public $recentJobExpires;
/**
* The number of minutes until completed jobs should be purged.
*
* @var int
*/
public $recentCompletedExpires;
/**
* The number of minutes until failed jobs should be purged.
*
@@ -66,6 +73,7 @@ class RedisJobRepository implements JobRepository
{
$this->redis = $redis;
$this->recentJobExpires = config('horizon.trim.recent', 60);
$this->recentCompletedExpires = config('horizon.trim.completed', 60);
$this->failedJobExpires = config('horizon.trim.failed', 10080);
$this->recentFailedJobExpires = config('horizon.trim.recent_failed', $this->failedJobExpires);
$this->monitoredJobExpires = config('horizon.trim.monitored', 10080);
@@ -403,7 +411,7 @@ class RedisJobRepository implements JobRepository
? $pipe->hmset($id, ['status' => 'failed'])
: $pipe->hmset($id, ['status' => 'completed', 'completed_at' => str_replace(',', '.', microtime(true))]);
$pipe->expireat($id, Chronos::now()->addMinutes($this->recentJobExpires)->getTimestamp());
$pipe->expireat($id, Chronos::now()->addMinutes($this->recentCompletedExpires)->getTimestamp());
}
/**

View File

@@ -9,6 +9,7 @@ use Illuminate\Events\CallQueuedListener;
use Illuminate\Mail\SendQueuedMailable;
use Illuminate\Notifications\SendQueuedNotifications;
use ReflectionClass;
use ReflectionProperty;
use stdClass;
class Tags
@@ -52,8 +53,8 @@ class Tags
protected static function tagsForListener($job)
{
return collect(
[static::extractListener($job), static::extractEvent($job),
])->map(function ($job) {
[static::extractListener($job), static::extractEvent($job)]
)->map(function ($job) {
return static::for($job);
})->collapse()->unique()->toArray();
}
@@ -104,10 +105,12 @@ class Tags
$models = [];
foreach ($targets as $target) {
$models[] = collect((new ReflectionClass($target))->getProperties())->map(function ($property) use ($target) {
$models[] = collect(
(new ReflectionClass($target))->getProperties()
)->map(function ($property) use ($target) {
$property->setAccessible(true);
$value = $property->getValue($target);
$value = static::getValue($property, $target);
if ($value instanceof Model) {
return [$value];
@@ -120,6 +123,22 @@ class Tags
return collect($models)->collapse()->unique();
}
/**
* Get the value of the given ReflectionProperty.
*
* @param \ReflectionProperty $property
* @param mixed $target
*/
protected static function getValue(ReflectionProperty $property, $target)
{
if (method_exists($property, 'isInitialized') &&
! $property->isInitialized($target)) {
return;
}
return $property->getValue($target);
}
/**
* Extract the listener from a queued job.
*

View File

@@ -2,8 +2,8 @@
namespace App\Providers;
use Laravel\Horizon\Horizon;
use Illuminate\Support\Facades\Gate;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\HorizonApplicationServiceProvider;
class HorizonServiceProvider extends HorizonApplicationServiceProvider
@@ -20,7 +20,7 @@ class HorizonServiceProvider extends HorizonApplicationServiceProvider
// Horizon::routeSmsNotificationsTo('15556667777');
// Horizon::routeMailNotificationsTo('example@example.com');
// Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
// Horizon::night();
}

View File

@@ -35,10 +35,10 @@ class GithubProvider extends AbstractProvider implements ProviderInterface
*/
protected function getUserByToken($token)
{
$userUrl = 'https://api.github.com/user?access_token='.$token;
$userUrl = 'https://api.github.com/user';
$response = $this->getHttpClient()->get(
$userUrl, $this->getRequestOptions()
$userUrl, $this->getRequestOptions($token)
);
$user = json_decode($response->getBody(), true);
@@ -58,11 +58,11 @@ class GithubProvider extends AbstractProvider implements ProviderInterface
*/
protected function getEmailByToken($token)
{
$emailsUrl = 'https://api.github.com/user/emails?access_token='.$token;
$emailsUrl = 'https://api.github.com/user/emails';
try {
$response = $this->getHttpClient()->get(
$emailsUrl, $this->getRequestOptions()
$emailsUrl, $this->getRequestOptions($token)
);
} catch (Exception $e) {
return;
@@ -94,11 +94,12 @@ class GithubProvider extends AbstractProvider implements ProviderInterface
*
* @return array
*/
protected function getRequestOptions()
protected function getRequestOptions($token)
{
return [
'headers' => [
'Accept' => 'application/vnd.github.v3+json',
'Authorization' => 'token '.$token,
],
];
}