oxidized-web 0.15.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of oxidized-web might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.rubocop.yml +22 -2
- data/CHANGELOG.md +30 -1
- data/README.md +13 -3
- data/Rakefile +9 -4
- data/docs/configuration.md +90 -0
- data/docs/development.md +42 -39
- data/lib/oxidized/web/public/scripts/oxidized.js +13 -13
- data/lib/oxidized/web/public/weblibs/bootstrap-icons.css +31 -3
- data/lib/oxidized/web/public/weblibs/bootstrap.bundle.js +20 -19
- data/lib/oxidized/web/public/weblibs/bootstrap.bundle.js.map +1 -1
- data/lib/oxidized/web/public/weblibs/bootstrap.css +110 -124
- data/lib/oxidized/web/public/weblibs/bootstrap.css.map +1 -1
- data/lib/oxidized/web/public/weblibs/bootstrap.js +20 -17
- data/lib/oxidized/web/public/weblibs/bootstrap.js.map +1 -1
- data/lib/oxidized/web/public/weblibs/buttons.bootstrap5.css +3 -3
- data/lib/oxidized/web/public/weblibs/buttons.colVis.js +14 -5
- data/lib/oxidized/web/public/weblibs/dataTables.bootstrap5.css +111 -17
- data/lib/oxidized/web/public/weblibs/dataTables.buttons.js +25 -7
- data/lib/oxidized/web/public/weblibs/dataTables.js +336 -106
- data/lib/oxidized/web/public/weblibs/dayjs-plugin-utc.min.js +1 -0
- data/lib/oxidized/web/public/weblibs/dayjs.min.js +1 -0
- data/lib/oxidized/web/public/weblibs/fonts/bootstrap-icons.woff +0 -0
- data/lib/oxidized/web/public/weblibs/fonts/bootstrap-icons.woff2 +0 -0
- data/lib/oxidized/web/version.rb +1 -1
- data/lib/oxidized/web/views/conf_search.haml +1 -1
- data/lib/oxidized/web/views/diffs.haml +6 -7
- data/lib/oxidized/web/views/head.haml +4 -0
- data/lib/oxidized/web/views/node.haml +3 -2
- data/lib/oxidized/web/views/nodes.haml +13 -5
- data/lib/oxidized/web/views/stats.haml +11 -3
- data/lib/oxidized/web/views/version.haml +1 -2
- data/lib/oxidized/web/views/versions.haml +11 -7
- data/lib/oxidized/web/webapp.rb +41 -29
- data/lib/oxidized/web.rb +72 -16
- data/oxidized-web.gemspec +22 -13
- data/package-lock.json +37 -25
- data/package.json +7 -5
- data/spec/spec_helper.rb +1 -0
- data/spec/web/node/show_spec.rb +100 -0
- data/spec/web/node/version_spec.rb +161 -0
- data/spec/{node_spec.rb → web/node_spec.rb} +1 -1
- data/spec/{nodes_spec.rb → web/nodes_spec.rb} +1 -1
- data/spec/{root_spec.rb → web/root_spec.rb} +1 -1
- data/spec/{webapp_spec.rb → web/webapp_spec.rb} +1 -1
- data/spec/web_spec.rb +98 -0
- metadata +69 -69
- data/.rubocop_todo.yml +0 -64
- data/spec/node_version_spec.rb +0 -102
@@ -0,0 +1 @@
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.dayjsPluginUTC=e():t.dayjsPluginUTC=e()}(this,function(){return function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=0)}([function(t,e,n){"use strict";function o(t){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function r(t){var e;switch(o(t)){case"string":return/Z$/.test(t)?0:(e=/([+-])(\d{2}):?(\d{2})/.exec(t))&&(+e[3]+60*e[2])*("+"===e[1]?1:-1);case"number":return Number.isNaN(t)?null:Math.abs(t)<16?60*t:t;default:return null}}n.r(e);var i=function(t,e,n){var o=String(t);return!o||o.length>=e?t:"".concat(Array(e+1-o.length).join(n)).concat(t)},s=function(t){var e=Math.abs(t),n=Math.floor(e/60),o=e%60;return"".concat(t<=0?"+":"-").concat(i(n,2,"0"),":").concat(i(o,2,"0"))};function u(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}var f=(new Date).getTimezoneOffset(),a=Date.prototype;function c(t){return 6e4*(t-(arguments.length>1&&void 0!==arguments[1]?arguments[1]:f))}var l=function(){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:new Date,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:e.getTimezoneOffset();!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.$d=new Date(e.getTime()-c(n)),this.$timezoneOffset=n}var e,n,o;return e=t,(n=[{key:"getTimezoneOffset",value:function(){return this.$timezoneOffset}},{key:"setTimezoneOffset",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.$timezoneOffset;this.$d.setTime(this.$d.getTime()+c(this.$timezoneOffset,t)),this.$timezoneOffset=t}}])&&u(e.prototype,n),o&&u(e,o),t}();["toDateString","toLocaleString","toLocaleDateString","toLocaleTimeString","setDate","setFullYear","setHours","setMilliseconds","setMinutes","setMonth","setSeconds","setTime","setYear","getDate","getDay","getFullYear","getHours","getMilliseconds","getMinutes","getMonth","getSeconds","getYear"].forEach(function(t){l.prototype[t]=function(){return a[t].apply(this.$d,arguments)}}),["toISOString","toUTCString","toGMTString","toJSON","getUTCDate","getUTCDay","getUTCFullYear","getUTCHours","getUTCMilliseconds","getUTCMinutes","getUTCMonth","getUTCSeconds","valueOf","getTime"].forEach(function(t){l.prototype[t]=function(){return a[t].apply(new Date(this.$d.getTime()+c(this.$timezoneOffset)),arguments)}}),["setUTCDate","setUTCFullYear","setUTCHours","setUTCMilliseconds","setUTCMinutes","setUTCMonth","setUTCSeconds"].forEach(function(t){l.prototype[t]=function(){var e=new Date(this.$d.getTime()+c(this.$timezoneOffset));a[t].apply(e,arguments),e.setTime(e.getTime()-c(this.$timezoneOffset)),this.$d=e}}),["toString","toTimeString"].forEach(function(t){l.prototype[t]=function(){return a[t].apply(this.$d,arguments).replace(/GMT(.*)$/,"GMT".concat(s(this.$timezoneOffset)))}});var d=l,p=!1,h=function(t,e){["clone","add","subtract","startOf"].forEach(function(n){t[n]=function(){var t=this.utcOffset();return e[n].apply(this,arguments).utcOffset(t)}}),t.utc=function(){return this.utcOffset(0)},t.local=function(){return this.utcOffset(-f)},t.utcOffset=function(t){if(void 0===t){var e=this.$d.getTimezoneOffset();return 0===e?0:-e}return null!==r(t)&&(this.$d.setTimezoneOffset(-r(t)),this.init()),this},t.toDate=function(){return new Date(this.$d.getTime())},t.isLocal=function(){return this.$d.getTimezoneOffset()===f},t.isUTC=function(){return 0===this.$d.getTimezoneOffset()},t.$set=function(){for(var t,n=this.$d.getTimezoneOffset(),o=arguments.length,r=new Array(o),i=0;i<o;i++)r[i]=arguments[i];return(t=e.$set).call.apply(t,[this].concat(r)),this.$d instanceof Date&&(this.$d=new d(this.$d,n)),this},t.parse=function(t){e.parse.call(this,t);var n=this.$d,o="string"==typeof t.date?r(t.date):null;this.$d=new d(n,null===o?f:-o),p&&this.local(),this.init()}};e.default=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=arguments.length>1?arguments[1]:void 0,n=arguments.length>2?arguments[2]:void 0;p=!!t.parseToLocal;var o=e.prototype,i=function(){};i.prototype=o;var s=new i;h(s,o),s.constructor=e.constructor,e.prototype=s,n.utc=function(t){var e=this(t);return"string"==typeof t&&null===r(t)&&(e.$d.$timezoneOffset=0),e.utc()}}}])});
|
@@ -0,0 +1 @@
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs=e()}(this,(function(){"use strict";var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return"["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return-t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,c),s=n-i<0,u=e.clone().add(r+(s?-1:1),c);return+(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return{M:c,y:h,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:f}[t]||String(t||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},g="en",D={};D[g]=M;var p="$isDayjsObject",S=function(t){return t instanceof _||!(!t||!t[p])},w=function t(e,n,r){var i;if(!e)return g;if("string"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split("-");if(!i&&u.length>1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return b},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<O(t)},m.$g=function(t,e,n){return b.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!b.u(e)||e,f=b.p(t),l=function(t,e){var i=b.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return b.w(n.toDate()[t].apply(n.toDate("s"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v="set"+(this.$u?"UTC":"");switch(f){case h:return r?l(1,0):l(31,11);case c:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+"Hours",0);case u:return $(v+"Minutes",1);case s:return $(v+"Seconds",2);case i:return $(v+"Milliseconds",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,!1)},m.$set=function(t,e){var n,o=b.p(t),f="set"+(this.$u?"UTC":""),l=(n={},n[a]=f+"Date",n[d]=f+"Date",n[c]=f+"Month",n[h]=f+"FullYear",n[u]=f+"Hours",n[s]=f+"Minutes",n[i]=f+"Seconds",n[r]=f+"Milliseconds",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===c||o===h){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[b.p(t)]()},m.add=function(r,f){var d,l=this;r=Number(r);var $=b.p(f),y=function(t){var e=O(l);return b.w(e.date(e.date()+Math.round(t*r)),l)};if($===c)return this.set(c,this.$M+r);if($===h)return this.set(h,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return b.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||"YYYY-MM-DDTHH:mm:ssZ",i=b.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,c=n.months,f=n.meridiem,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},d=function(t){return b.s(s%12||12,t,"0")},$=f||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r};return r.replace(y,(function(t,r){return r||function(t){switch(t){case"YY":return String(e.$y).slice(-2);case"YYYY":return b.s(e.$y,4,"0");case"M":return a+1;case"MM":return b.s(a+1,2,"0");case"MMM":return h(n.monthsShort,a,c,3);case"MMMM":return h(c,a);case"D":return e.$D;case"DD":return b.s(e.$D,2,"0");case"d":return String(e.$W);case"dd":return h(n.weekdaysMin,e.$W,o,2);case"ddd":return h(n.weekdaysShort,e.$W,o,3);case"dddd":return o[e.$W];case"H":return String(s);case"HH":return b.s(s,2,"0");case"h":return d(1);case"hh":return d(2);case"a":return $(s,u,!0);case"A":return $(s,u,!1);case"m":return String(u);case"mm":return b.s(u,2,"0");case"s":return String(e.$s);case"ss":return b.s(e.$s,2,"0");case"SSS":return b.s(e.$ms,3,"0");case"Z":return i}return null}(t)||i.replace(":","")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=this,M=b.p(d),m=O(r),v=(m.utcOffset()-this.utcOffset())*e,g=this-m,D=function(){return b.m(y,m)};switch(M){case h:$=D()/12;break;case c:$=D();break;case f:$=D()/3;break;case o:$=(g-v)/6048e5;break;case a:$=(g-v)/864e5;break;case u:$=g/n;break;case s:$=g/e;break;case i:$=g/t;break;default:$=g}return l?$:b.a($)},m.daysInMonth=function(){return this.endOf(c).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=w(t,e,!0);return r&&(n.$L=r),n},m.clone=function(){return b.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),k=_.prototype;return O.prototype=k,[["$ms",r],["$s",i],["$m",s],["$H",u],["$W",a],["$M",c],["$y",h],["$D",d]].forEach((function(t){k[t[1]]=function(e){return this.$g(e,t[0],t[1])}})),O.extend=function(t,e){return t.$i||(t(e,_,O),t.$i=!0),O},O.locale=w,O.isDayjs=S,O.unix=function(t){return O(1e3*t)},O.en=D[g],O.Ls=D,O.p={},O}));
|
Binary file
|
Binary file
|
data/lib/oxidized/web/version.rb
CHANGED
@@ -10,9 +10,8 @@
|
|
10
10
|
%b #{@info[:node]}
|
11
11
|
.row
|
12
12
|
.col-sm-12
|
13
|
-
- date_version = Time.parse @info[:date]
|
14
13
|
Date of version:
|
15
|
-
%span.time
|
14
|
+
%span.time{ epoch: @info[:time].to_i }= @info[:time]
|
16
15
|
.row
|
17
16
|
.col-sm-12
|
18
17
|
Number of lines changed:
|
@@ -22,7 +21,7 @@
|
|
22
21
|
.row
|
23
22
|
.col-sm-6
|
24
23
|
- params = "node=#{@info[:node]}&group=#{@info[:group]}&oid=#{@info[:oid]}"
|
25
|
-
- params = "#{params}&
|
24
|
+
- params = "#{params}&epoch=#{@info[:time].to_i}&num=#{@info[:num]}"
|
26
25
|
%form{action: url_for("/node/version/diffs?#{params}"), method: 'post', role: 'form'}
|
27
26
|
.form-group
|
28
27
|
%select.form-select#oid2{name: 'oid2'}
|
@@ -30,9 +29,9 @@
|
|
30
29
|
- num = @oids_dates.count + 1
|
31
30
|
- next_id = false
|
32
31
|
- @oids_dates.each do |x|
|
33
|
-
%option{value: x[:oid]} Version #{num -= 1} (#{time_from_now x[:
|
32
|
+
%option{value: x[:oid]} Version #{num -= 1} (#{time_from_now x[:time]})
|
34
33
|
- if (x[:oid].to_s == @info[:oid2]) || (next_id)
|
35
|
-
- diff2 = {num: num,
|
34
|
+
- diff2 = {num: num, time: x[:time]}
|
36
35
|
- next_id = false
|
37
36
|
- elsif (x[:oid].to_s == @info[:oid]) && !(@info[:oid2])
|
38
37
|
- next_id = true
|
@@ -41,8 +40,8 @@
|
|
41
40
|
|
42
41
|
.row
|
43
42
|
.col-sm-12
|
44
|
-
.old_version_title Version #{diff2[:num]} (#{time_from_now diff2[:
|
45
|
-
.new_version_title Version #{@info[:num]} (#{time_from_now @info[:
|
43
|
+
.old_version_title Version #{diff2[:num]} (#{time_from_now diff2[:time]})
|
44
|
+
.new_version_title Version #{@info[:num]} (#{time_from_now @info[:time]})
|
46
45
|
|
47
46
|
.row
|
48
47
|
.col-sm-12
|
@@ -25,5 +25,9 @@
|
|
25
25
|
%script{src: url_for('/weblibs/dataTables.buttons.js')}
|
26
26
|
%script{src: url_for('/weblibs/buttons.bootstrap5.js')}
|
27
27
|
%script{src: url_for('/weblibs/buttons.colVis.js')}
|
28
|
+
-# Day.js
|
29
|
+
%script{src: url_for('/weblibs/dayjs.min.js')}
|
30
|
+
%script{src: url_for('/weblibs/dayjs-plugin-utc.min.js')}
|
31
|
+
%script dayjs.extend(dayjsPluginUTC.default)
|
28
32
|
-# Oxidized customisation
|
29
33
|
%script{src: url_for('/scripts/oxidized.js')}
|
@@ -14,7 +14,8 @@
|
|
14
14
|
%a.link-dark.link-underline-opacity-0{title: 'update',
|
15
15
|
href: url_for("/node/next/#{@data[:full_name]}")}
|
16
16
|
%i.bi.bi-repeat
|
17
|
-
- out = '';PP.pp(@data,out)
|
18
17
|
%pre.bg-body-tertiary.border.border-secondary-subtle.rounded
|
19
|
-
|
18
|
+
%code
|
19
|
+
=escape_once(JSON.pretty_generate(@data))
|
20
|
+
|
20
21
|
|
@@ -5,10 +5,13 @@
|
|
5
5
|
%form.float-end#oxButtons
|
6
6
|
%button.btn.btn-primary{type: 'button', onclick: 'history.go();'}
|
7
7
|
%i.bi.bi-arrow-clockwise
|
8
|
-
Refresh
|
8
|
+
Refresh page
|
9
|
+
%button.btn.btn-primary#resetSearch{type: 'button'}
|
10
|
+
%i.bi.bi-eraser-fill
|
11
|
+
Reset search
|
9
12
|
%button.btn.btn-primary#reload{type: 'button'}
|
10
13
|
%i.bi.bi-repeat
|
11
|
-
|
14
|
+
Reload node list from source backend
|
12
15
|
|
13
16
|
.hidden.alert#flashMessage
|
14
17
|
|
@@ -39,8 +42,8 @@
|
|
39
42
|
%td
|
40
43
|
%div{title: node[:status], class: node[:status]}
|
41
44
|
%span{style: 'visibility: hidden'}#{node[:status]}
|
42
|
-
%td.time= node[:time]
|
43
|
-
%td.time= node[:mtime]
|
45
|
+
%td.time{ epoch: node[:time].to_i }= node[:time]
|
46
|
+
%td.time{ epoch: node[:mtime].to_i }= node[:mtime]
|
44
47
|
%td
|
45
48
|
%a.link-dark.link-underline-opacity-0{title: 'Configuration',
|
46
49
|
href: url_for("/node/fetch/#{node[:full_name]}")}
|
@@ -76,11 +79,16 @@
|
|
76
79
|
className: 'btn-primary',
|
77
80
|
}],
|
78
81
|
autoWidth: false,
|
79
|
-
lengthMenu: [20, 50, 250, 500, { label: 'All', value: -1 }]
|
82
|
+
lengthMenu: [20, 50, 250, 500, { label: 'All', value: -1 }],
|
83
|
+
stateSave: true,
|
80
84
|
});
|
81
85
|
|
82
86
|
table.buttons(0,0).container().prependTo($('#oxButtons'));
|
83
87
|
table.buttons(0,0).nodes().removeClass('btn-secondary');
|
88
|
+
|
89
|
+
$('#resetSearch').on('click', function() {
|
90
|
+
table.search('').draw();
|
91
|
+
});
|
84
92
|
});
|
85
93
|
|
86
94
|
|
@@ -7,7 +7,10 @@
|
|
7
7
|
%form.float-end#oxButtons
|
8
8
|
%button.btn.btn-primary{type: 'button', onclick: 'history.go();'}
|
9
9
|
%i.bi.bi-arrow-clockwise
|
10
|
-
Refresh
|
10
|
+
Refresh page
|
11
|
+
%button.btn.btn-primary#resetSearch{type: 'button'}
|
12
|
+
%i.bi.bi-eraser-fill
|
13
|
+
Reset search
|
11
14
|
|
12
15
|
.row
|
13
16
|
.table-responsive
|
@@ -76,8 +79,8 @@
|
|
76
79
|
%td
|
77
80
|
%div{title: status, class: status}
|
78
81
|
%span{style: 'visibility: hidden'}#{status}
|
79
|
-
%td.time= last_success
|
80
|
-
%td.time= last_failure
|
82
|
+
%td.time{ epoch: last_success.to_i }= last_success
|
83
|
+
%td.time{ epoch: last_failure.to_i }= last_failure
|
81
84
|
|
82
85
|
:javascript
|
83
86
|
$(function() {
|
@@ -102,8 +105,13 @@
|
|
102
105
|
}],
|
103
106
|
autoWidth: false,
|
104
107
|
lengthMenu: [20, 50, 250, 500, { label: 'All', value: -1 }],
|
108
|
+
stateSave: true,
|
105
109
|
});
|
106
110
|
|
107
111
|
table.buttons(0,0).container().prependTo($('#oxButtons'));
|
108
112
|
table.buttons(0,0).nodes().removeClass('btn-secondary');
|
113
|
+
|
114
|
+
$('#resetSearch').on('click', function() {
|
115
|
+
table.search('').draw();
|
116
|
+
});
|
109
117
|
});
|
@@ -13,9 +13,8 @@
|
|
13
13
|
%a.btn.btn-primary{:href => "#{request.path}?#{request.query_string}&format=text"} raw
|
14
14
|
.row
|
15
15
|
.col-sm-12
|
16
|
-
- date_version = Time.parse @info[:date]
|
17
16
|
Date of version:
|
18
|
-
%span.time
|
17
|
+
%span.time{ epoch: @info[:time].to_i }= @info[:time]
|
19
18
|
|
20
19
|
.row
|
21
20
|
.col-sm-12
|
@@ -8,7 +8,8 @@
|
|
8
8
|
%form.float-end#oxButtons
|
9
9
|
%button.btn.btn-primary{type: 'button', onclick: 'history.go();'}
|
10
10
|
%i.bi.bi-arrow-clockwise
|
11
|
-
Refresh
|
11
|
+
Refresh page
|
12
|
+
|
12
13
|
.row
|
13
14
|
.table-responsive
|
14
15
|
%table.table.table-sm.table-striped.table-hover#versionsTable
|
@@ -24,17 +25,18 @@
|
|
24
25
|
- @data.each do |x|
|
25
26
|
%tr
|
26
27
|
%td #{nb -= 1}
|
27
|
-
%td
|
28
|
-
%td #{time_from_now x[:
|
28
|
+
%td.time{ epoch: x[:time].to_i }= x[:time]
|
29
|
+
%td #{time_from_now x[:time]}
|
29
30
|
%td
|
30
31
|
- params = "node=#{@node}&group=#{@group}&oid=#{x[:oid]}"
|
31
|
-
- params = "#{params}&
|
32
|
+
- params = "#{params}&epoch=#{x[:time].to_i}&num=#{nb}"
|
32
33
|
%a.link-dark.link-underline-opacity-0{title: 'configuration',
|
33
34
|
href: url_for("/node/version/view?#{params}")}
|
34
35
|
%i.bi.bi-cloud-download
|
35
36
|
|
36
|
-
|
37
|
-
%
|
37
|
+
- if nb > 1
|
38
|
+
%a.link-dark.link-underline-opacity-0{title: 'Compare with previous version', href: url_for("/node/version/diffs?#{params}")}
|
39
|
+
%i.bi.bi-file-earmark-diff
|
38
40
|
|
39
41
|
:javascript
|
40
42
|
$(function() {
|
@@ -46,6 +48,8 @@
|
|
46
48
|
targets: '_all',
|
47
49
|
className: 'text-start'
|
48
50
|
}
|
49
|
-
]
|
51
|
+
],
|
52
|
+
stateSave: true,
|
53
|
+
searching: false
|
50
54
|
});
|
51
55
|
});
|
data/lib/oxidized/web/webapp.rb
CHANGED
@@ -2,10 +2,6 @@ require 'sinatra/base'
|
|
2
2
|
require 'sinatra/json'
|
3
3
|
require 'sinatra/url_for'
|
4
4
|
require 'tilt/haml'
|
5
|
-
# We need PP in node.haml, but rubocop can't see this
|
6
|
-
# rubocop:disable Lint/RedundantRequireStatement
|
7
|
-
require 'pp'
|
8
|
-
# rubocop:enable Lint/RedundantRequireStatement
|
9
5
|
require 'htmlentities'
|
10
6
|
require 'charlock_holmes'
|
11
7
|
module Oxidized
|
@@ -28,7 +24,7 @@ module Oxidized
|
|
28
24
|
# :filter can be "group" or "model"
|
29
25
|
# URL: /nodes/group/<GroupName>[.json]
|
30
26
|
# URL: /nodes/model/<ModelName>[.json]
|
31
|
-
# an optional .json
|
27
|
+
# an optional .json extension returns the data as JSON
|
32
28
|
#
|
33
29
|
# as GroupName can include /, we use splat to match its value
|
34
30
|
# and extract the optional ".json" with route_parse
|
@@ -122,7 +118,7 @@ module Oxidized
|
|
122
118
|
# use this to attach author/email/message to commit
|
123
119
|
put '/node/next/?*?/:node' do
|
124
120
|
node, @json = route_parse :node
|
125
|
-
opt = JSON.
|
121
|
+
opt = JSON.parse request.body.read
|
126
122
|
nodes.next node, opt
|
127
123
|
redirect url_for('/nodes') unless @json
|
128
124
|
@data = 'ok'
|
@@ -131,7 +127,7 @@ module Oxidized
|
|
131
127
|
|
132
128
|
get '/node/show/:node' do
|
133
129
|
node, @json = route_parse :node
|
134
|
-
@data = nodes.show
|
130
|
+
@data = filter_node_vars(nodes.show(node))
|
135
131
|
out :node
|
136
132
|
end
|
137
133
|
|
@@ -161,7 +157,7 @@ module Oxidized
|
|
161
157
|
node: node,
|
162
158
|
group: params[:group],
|
163
159
|
oid: params[:oid],
|
164
|
-
|
160
|
+
time: Time.at(params[:epoch].to_i),
|
165
161
|
num: params[:num]
|
166
162
|
}
|
167
163
|
|
@@ -179,7 +175,12 @@ module Oxidized
|
|
179
175
|
get '/node/version/diffs' do
|
180
176
|
node, @json = route_parse :node
|
181
177
|
@data = nil
|
182
|
-
@info = { node: node,
|
178
|
+
@info = { node: node,
|
179
|
+
group: params[:group],
|
180
|
+
oid: params[:oid],
|
181
|
+
time: Time.at(params[:epoch].to_i),
|
182
|
+
num: params[:num],
|
183
|
+
num2: (params[:num].to_i - 1) }
|
183
184
|
group = nil
|
184
185
|
group = @info[:group] if @info[:group] != ''
|
185
186
|
@oids_dates = nodes.version node, group
|
@@ -204,7 +205,7 @@ module Oxidized
|
|
204
205
|
@stat = @data[:stat]
|
205
206
|
@data = @data[:patch]
|
206
207
|
else
|
207
|
-
@data = '
|
208
|
+
@data = 'No diff available'
|
208
209
|
end
|
209
210
|
@diff = diff_view @data
|
210
211
|
out :diffs
|
@@ -212,7 +213,7 @@ module Oxidized
|
|
212
213
|
|
213
214
|
# used for diff between 2 distant commit
|
214
215
|
post '/node/version/diffs' do
|
215
|
-
redirect url_for("/node/version/diffs?node=#{params[:node]}&group=#{params[:group]}&oid=#{params[:oid]}&
|
216
|
+
redirect url_for("/node/version/diffs?node=#{params[:node]}&group=#{params[:group]}&oid=#{params[:oid]}&epoch=#{params[:epoch]}&num=#{params[:num]}&oid2=#{params[:oid2]}")
|
216
217
|
end
|
217
218
|
|
218
219
|
# Taken von Haml 5.0, so it still works in 6.0
|
@@ -257,26 +258,23 @@ module Oxidized
|
|
257
258
|
[e.join('.'), json]
|
258
259
|
end
|
259
260
|
|
260
|
-
# give the time
|
261
|
+
# give the time elapsed between now and a date (Time object)
|
261
262
|
def time_from_now(date)
|
262
|
-
if date
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
"#{mm} min #{ss} sec ago"
|
277
|
-
end
|
263
|
+
return "no time specified" if date.nil?
|
264
|
+
|
265
|
+
raise "time_from_now needs a Time object" unless date.instance_of? Time
|
266
|
+
|
267
|
+
t = (Time.now - date).to_i
|
268
|
+
mm, ss = t.divmod(60)
|
269
|
+
hh, mm = mm.divmod(60)
|
270
|
+
dd, hh = hh.divmod(24)
|
271
|
+
if dd.positive?
|
272
|
+
"#{dd} days #{hh} hours ago"
|
273
|
+
elsif hh.positive?
|
274
|
+
"#{hh} hours #{mm} min ago"
|
275
|
+
else
|
276
|
+
"#{mm} min #{ss} sec ago"
|
278
277
|
end
|
279
|
-
date
|
280
278
|
end
|
281
279
|
|
282
280
|
# method the give diffs in separate view (the old and the new) as in github
|
@@ -329,6 +327,20 @@ module Oxidized
|
|
329
327
|
'The text contains binary values - cannot display'
|
330
328
|
end
|
331
329
|
end
|
330
|
+
|
331
|
+
def filter_node_vars(serialized_node)
|
332
|
+
# Make a deep copy of the data, so we do not impact oxidized
|
333
|
+
data = Marshal.load(Marshal.dump(serialized_node))
|
334
|
+
|
335
|
+
hide_node_vars = settings.configuration[:hide_node_vars]
|
336
|
+
if data[:vars].is_a?(Hash) && hide_node_vars&.any?
|
337
|
+
hide_node_vars.each do |key|
|
338
|
+
data[:vars][key] = '<hidden>' if data[:vars].has_key?(key)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
data
|
343
|
+
end
|
332
344
|
end
|
333
345
|
end
|
334
346
|
end
|
data/lib/oxidized/web.rb
CHANGED
@@ -1,27 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
4
|
+
require 'puma'
|
2
5
|
|
3
6
|
module Oxidized
|
4
7
|
module API
|
5
8
|
class Web
|
6
|
-
|
9
|
+
include SemanticLogger::Loggable
|
10
|
+
|
7
11
|
attr_reader :thread
|
8
12
|
|
9
|
-
|
13
|
+
DEFAULT_HOST = '127.0.0.1'
|
14
|
+
DEFAULT_PORT = 8888
|
15
|
+
DEFAULT_URI_PREFIX = ''
|
16
|
+
|
17
|
+
def initialize(nodes, configuration)
|
10
18
|
require 'oxidized/web/webapp'
|
11
|
-
|
12
|
-
addr, _, port = listen.rpartition ':'
|
13
|
-
unless port
|
14
|
-
port = addr
|
15
|
-
addr = nil
|
16
|
-
end
|
17
|
-
uri = "/#{uri}"
|
18
|
-
@opts = {
|
19
|
-
Host: addr,
|
20
|
-
Port: port
|
21
|
-
}
|
19
|
+
@configuration = self.class.parse_configuration(configuration)
|
22
20
|
WebApp.set :nodes, nodes
|
21
|
+
WebApp.set :configuration, @configuration
|
22
|
+
WebApp.set :host_authorization, {
|
23
|
+
permitted_hosts: @configuration[:vhosts]
|
24
|
+
}
|
25
|
+
uri_prefix = @configuration[:uri_prefix]
|
23
26
|
@app = Rack::Builder.new do
|
24
|
-
map
|
27
|
+
map uri_prefix do
|
25
28
|
run WebApp
|
26
29
|
end
|
27
30
|
end
|
@@ -29,9 +32,62 @@ module Oxidized
|
|
29
32
|
|
30
33
|
def run
|
31
34
|
@thread = Thread.new do
|
32
|
-
|
33
|
-
|
35
|
+
@server = Puma::Server.new @app
|
36
|
+
addr = @configuration[:addr]
|
37
|
+
port = @configuration[:port]
|
38
|
+
@server.add_tcp_listener addr, port
|
39
|
+
logger.info "Oxidized-web server listening on #{addr}:#{port}"
|
40
|
+
@server.run.join
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.parse_configuration(configuration)
|
45
|
+
if configuration.instance_of? Asetus::ConfigStruct
|
46
|
+
parse_new_configuration(configuration)
|
47
|
+
else
|
48
|
+
parse_legacy_configuration(configuration)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# New configuration style: extensions.oxidized-web
|
53
|
+
def self.parse_new_configuration(configuration)
|
54
|
+
hide_node_vars = configuration.hide_node_vars? || []
|
55
|
+
unless hide_node_vars.is_a?(Array)
|
56
|
+
logger.error "hide_node_vars must be a list of strings"
|
57
|
+
hide_node_vars = []
|
58
|
+
end
|
59
|
+
hide_node_vars = hide_node_vars.map(&:to_sym)
|
60
|
+
{
|
61
|
+
addr: configuration.listen? || DEFAULT_HOST,
|
62
|
+
port: configuration.port? || DEFAULT_PORT,
|
63
|
+
uri_prefix: normalize_uri(configuration.url_prefix? ||
|
64
|
+
DEFAULT_URI_PREFIX),
|
65
|
+
vhosts: configuration.vhosts? || [],
|
66
|
+
hide_node_vars: hide_node_vars
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
# Legacy configuration style: "rest: 127.0.0.1:8888/prefix"
|
71
|
+
def self.parse_legacy_configuration(configuration)
|
72
|
+
listen, uri_prefix = configuration.split('/', 2)
|
73
|
+
addr, _, port = listen.rpartition ':'
|
74
|
+
unless port
|
75
|
+
port = addr
|
76
|
+
addr = nil
|
34
77
|
end
|
78
|
+
{
|
79
|
+
addr: addr,
|
80
|
+
port: port.to_i,
|
81
|
+
uri_prefix: normalize_uri(uri_prefix || DEFAULT_URI_PREFIX),
|
82
|
+
vhosts: [],
|
83
|
+
hide_node_vars: []
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.normalize_uri(uri_prefix)
|
88
|
+
return '/' if uri_prefix.empty?
|
89
|
+
|
90
|
+
uri_prefix.start_with?('/') ? uri_prefix : "/#{uri_prefix}"
|
35
91
|
end
|
36
92
|
end
|
37
93
|
end
|
data/oxidized-web.gemspec
CHANGED
@@ -20,16 +20,26 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.required_ruby_version = '>= 3.1'
|
22
22
|
|
23
|
-
|
23
|
+
# Gemspec strategy
|
24
|
+
#
|
25
|
+
# For dependency and optional dependencies, we try to set the minimal
|
26
|
+
# dependency lower than the Ubuntu Noble or Debian Bookworm package version,
|
27
|
+
# so that native packages can be used.
|
28
|
+
# We limit the maximal version so that dependabot can warn about new versions
|
29
|
+
# and we can test them before activating them in Oxidized.
|
30
|
+
#
|
31
|
+
# development dependencies are set to the latest minor version of a library
|
32
|
+
# and updated after having tested them
|
33
|
+
|
34
|
+
s.add_dependency 'charlock_holmes', '>= 0.7.5', '< 0.8.0'
|
24
35
|
s.add_dependency 'emk-sinatra-url-for', '~> 0.2'
|
25
|
-
s.add_dependency 'haml', '
|
26
|
-
s.add_dependency 'htmlentities', '
|
27
|
-
s.add_dependency 'json', '
|
28
|
-
s.add_dependency '
|
29
|
-
s.add_dependency '
|
30
|
-
s.add_dependency '
|
31
|
-
s.add_dependency 'sinatra',
|
32
|
-
s.add_dependency 'sinatra-contrib', '>= 1.4.6'
|
36
|
+
s.add_dependency 'haml', '>= 6.0.0', '< 6.4.0'
|
37
|
+
s.add_dependency 'htmlentities', '>= 4.3.0', '< 4.4.0'
|
38
|
+
s.add_dependency 'json', '>= 2.3.0', '< 2.14.0'
|
39
|
+
s.add_dependency 'oxidized', '~> 0.34.0'
|
40
|
+
s.add_dependency 'puma', '~> 6.6.0'
|
41
|
+
s.add_dependency 'sinatra', '~> 4.1.1'
|
42
|
+
s.add_dependency 'sinatra-contrib', '~> 4.1.1'
|
33
43
|
|
34
44
|
s.add_development_dependency 'bundler', '~> 2.2'
|
35
45
|
s.add_development_dependency 'minitest', '~> 5.18'
|
@@ -37,11 +47,10 @@ Gem::Specification.new do |s|
|
|
37
47
|
s.add_development_dependency 'rack-test', '~> 2.1'
|
38
48
|
s.add_development_dependency 'rails_best_practices', '~> 1.19'
|
39
49
|
s.add_development_dependency 'rake', '~> 13.0'
|
40
|
-
s.add_development_dependency 'rubocop', '~> 1.
|
41
|
-
s.add_development_dependency 'rubocop-minitest', '~> 0.
|
42
|
-
s.add_development_dependency 'rubocop-rails', '~> 2.
|
50
|
+
s.add_development_dependency 'rubocop', '~> 1.78.0'
|
51
|
+
s.add_development_dependency 'rubocop-minitest', '~> 0.38.0'
|
52
|
+
s.add_development_dependency 'rubocop-rails', '~> 2.32.0'
|
43
53
|
s.add_development_dependency 'rubocop-rake', '~> 0.7.1'
|
44
54
|
s.add_development_dependency 'simplecov', '~> 0.22.0'
|
45
|
-
s.add_development_dependency 'simplecov-cobertura', '~> 2.1.0'
|
46
55
|
s.add_development_dependency 'simplecov-html', '~> 0.13.1'
|
47
56
|
end
|