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.

Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.rubocop.yml +22 -2
  4. data/CHANGELOG.md +30 -1
  5. data/README.md +13 -3
  6. data/Rakefile +9 -4
  7. data/docs/configuration.md +90 -0
  8. data/docs/development.md +42 -39
  9. data/lib/oxidized/web/public/scripts/oxidized.js +13 -13
  10. data/lib/oxidized/web/public/weblibs/bootstrap-icons.css +31 -3
  11. data/lib/oxidized/web/public/weblibs/bootstrap.bundle.js +20 -19
  12. data/lib/oxidized/web/public/weblibs/bootstrap.bundle.js.map +1 -1
  13. data/lib/oxidized/web/public/weblibs/bootstrap.css +110 -124
  14. data/lib/oxidized/web/public/weblibs/bootstrap.css.map +1 -1
  15. data/lib/oxidized/web/public/weblibs/bootstrap.js +20 -17
  16. data/lib/oxidized/web/public/weblibs/bootstrap.js.map +1 -1
  17. data/lib/oxidized/web/public/weblibs/buttons.bootstrap5.css +3 -3
  18. data/lib/oxidized/web/public/weblibs/buttons.colVis.js +14 -5
  19. data/lib/oxidized/web/public/weblibs/dataTables.bootstrap5.css +111 -17
  20. data/lib/oxidized/web/public/weblibs/dataTables.buttons.js +25 -7
  21. data/lib/oxidized/web/public/weblibs/dataTables.js +336 -106
  22. data/lib/oxidized/web/public/weblibs/dayjs-plugin-utc.min.js +1 -0
  23. data/lib/oxidized/web/public/weblibs/dayjs.min.js +1 -0
  24. data/lib/oxidized/web/public/weblibs/fonts/bootstrap-icons.woff +0 -0
  25. data/lib/oxidized/web/public/weblibs/fonts/bootstrap-icons.woff2 +0 -0
  26. data/lib/oxidized/web/version.rb +1 -1
  27. data/lib/oxidized/web/views/conf_search.haml +1 -1
  28. data/lib/oxidized/web/views/diffs.haml +6 -7
  29. data/lib/oxidized/web/views/head.haml +4 -0
  30. data/lib/oxidized/web/views/node.haml +3 -2
  31. data/lib/oxidized/web/views/nodes.haml +13 -5
  32. data/lib/oxidized/web/views/stats.haml +11 -3
  33. data/lib/oxidized/web/views/version.haml +1 -2
  34. data/lib/oxidized/web/views/versions.haml +11 -7
  35. data/lib/oxidized/web/webapp.rb +41 -29
  36. data/lib/oxidized/web.rb +72 -16
  37. data/oxidized-web.gemspec +22 -13
  38. data/package-lock.json +37 -25
  39. data/package.json +7 -5
  40. data/spec/spec_helper.rb +1 -0
  41. data/spec/web/node/show_spec.rb +100 -0
  42. data/spec/web/node/version_spec.rb +161 -0
  43. data/spec/{node_spec.rb → web/node_spec.rb} +1 -1
  44. data/spec/{nodes_spec.rb → web/nodes_spec.rb} +1 -1
  45. data/spec/{root_spec.rb → web/root_spec.rb} +1 -1
  46. data/spec/{webapp_spec.rb → web/webapp_spec.rb} +1 -1
  47. data/spec/web_spec.rb +98 -0
  48. metadata +69 -69
  49. data/.rubocop_todo.yml +0 -64
  50. 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}));
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Oxidized
4
4
  module API
5
- WEB_VERSION = '0.15.1'
5
+ WEB_VERSION = '0.17.0'
6
6
  end
7
7
  end
@@ -9,7 +9,7 @@
9
9
  %form.float-end#oxButtons
10
10
  %button.btn.btn-primary{type: 'button', onclick: 'history.go();'}
11
11
  %i.bi.bi-arrow-clockwise
12
- Refresh
12
+ Refresh page
13
13
 
14
14
  .row
15
15
  .table-responsive
@@ -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 #{date_version.strftime("%d-%m-%y at %r")}
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}&date=#{@info[:date]}&num=#{@info[:num]}"
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[:date]})
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, date: x[:date]}
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[:date]})
45
- .new_version_title Version #{@info[:num]} (#{time_from_now @info[:date]})
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
- = preserve "#{out}"
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
- Update node list
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 #{date_version.strftime('%Y-%m-%d %H:%M:%S %Z')}
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 #{x[:date]}
28
- %td #{time_from_now x[:date]}
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}&date=#{x[:date]}&num=#{nb}"
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
  &nbsp;&nbsp;
36
- %a.link-dark.link-underline-opacity-0{title: 'diff', href: url_for("/node/version/diffs?#{params}")}
37
- %i.bi.bi-file-earmark-diff
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
  });
@@ -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 extention returns the data as 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.load request.body.read
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 node
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
- date: params[:date],
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, group: params[:group], oid: params[:oid], date: params[:date], num: params[:num], num2: (params[:num].to_i - 1) }
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 = 'no available'
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]}&date=#{params[:date]}&num=#{params[:num]}&oid2=#{params[:oid2]}")
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 enlapsed between now and a date
261
+ # give the time elapsed between now and a date (Time object)
261
262
  def time_from_now(date)
262
- if date
263
- # if the + or - is missing, insert +
264
- date.insert(21, '+') unless date =~ /[-+]/
265
- date = DateTime.parse date
266
- now = DateTime.now.new_offset(0)
267
- t = ((now - date) * 24 * 60 * 60).to_i
268
- mm, ss = t.divmod(60)
269
- hh, mm = mm.divmod(60)
270
- dd, hh = hh.divmod(24)
271
- date = 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"
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
- require 'rack/handler/puma'
9
+ include SemanticLogger::Loggable
10
+
7
11
  attr_reader :thread
8
12
 
9
- def initialize(nodes, listen)
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
- listen, uri = listen.split '/'
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 uri do
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
- Rack::Handler::Puma.run @app, **@opts
33
- exit!
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
- s.add_dependency 'charlock_holmes', '~> 0.7.5'
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', '~> 6.0'
26
- s.add_dependency 'htmlentities', '~> 4.3'
27
- s.add_dependency 'json', '~> 2.3'
28
- s.add_dependency 'ostruct', '~> 0.6'
29
- s.add_dependency 'oxidized', '~> 0.31'
30
- s.add_dependency 'puma', '>= 3.11.4'
31
- s.add_dependency 'sinatra', '>= 1.4.6'
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.72.1'
41
- s.add_development_dependency 'rubocop-minitest', '~> 0.37.1'
42
- s.add_development_dependency 'rubocop-rails', '~> 2.30.0'
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