Browse Source

Add more stats — Improve CSS

Norore 3 months ago
parent
commit
cfe2867606
  1. 10
      app/controllers/statistics_controller.rb
  2. 5
      app/javascript/css/_mixin.scss
  3. 208
      app/javascript/css/application.scss
  4. 44
      app/javascript/js/stats/ciphers.js
  5. 46
      app/javascript/js/stats/tls.js
  6. 4
      app/javascript/packs/application.js
  7. 2
      app/views/check/processing.html.erb
  8. 6
      app/views/check/show.html.erb
  9. 2
      app/views/site/index.html.erb
  10. 2
      app/views/site/suite_index.html.erb
  11. 2
      app/views/ssh/index.html.erb
  12. 156
      app/views/statistics/index.html.erb
  13. 2
      app/views/tls/index.html.erb
  14. 9
      config/routes.rb

10
app/controllers/statistics_controller.rb

@ -10,4 +10,14 @@ class StatisticsController < ApplicationController
end
end
end
def ciphers
service = params.fetch :id
render json: Stat.where(name: "ciphers_for_#{service}").order(date: :desc).first.data
end
def tls
service = params.fetch :id
render json: Stat.where(name: "tls_for_#{service}").order(date: :desc).first.data
end
end

5
app/javascript/css/_mixin.scss

@ -0,0 +1,5 @@
$dark: #3f4853;
$link-color: #135262;
$nav-pills-link-active-bg: #535d6c;

208
app/javascript/css/application.scss

@ -1,3 +1,4 @@
@import "mixin";
//@import "~bootstrap/scss/bootstrap";
@import "~bootstrap/scss/functions";
@ -44,36 +45,45 @@
$fa-font-path: "~font-awesome/fonts";
@import "~font-awesome/scss/font-awesome";
body {
background-color: #fafafa;
}
/** Headings */
h1, h2, h3, h4 {
color: #3f4853;
}
.badge-error, .progress-bar-error {
background-color: #000;
background-color: #000;
}
.badge-critical, .progress-bar-critical {
background-color: #000;
background-color: #000;
}
table.center {
td {
text-align: center;
td {
text-align: center;
&.left {
text-align: left;
}
}
&.left {
text-align: left;
}
}
}
td.error {
background-color: #ddd;
background-color: #ddd;
}
.progress {
margin: 0;
margin: 0;
}
#check,
#ssh_check,
#tls_check {
margin-top: 100px;
margin-top: 100px;
}
$color-critical: #d9534f;
@ -84,129 +94,179 @@ $color-best: #8db457;
$color-great: #5cb85c;
.badge-state-critical, td.badge-state-critical {
background-color: $color-critical;
background-color: $color-critical;
}
.badge-state-error, td.badge-state-error {
background-color: $color-error;
background-color: $color-error;
}
.badge-state-warning, td.badge-state-warning {
background-color: $color-warning;
background-color: $color-warning;
}
.badge-state-good, td.badge-state-good {
background-color: $color-good;
background-color: $color-good;
}
.badge-state-best, td.badge-state-best {
background-color: $color-best;
background-color: $color-best;
}
.badge-state-great, td.badge-state-great {
background-color: $color-great;
background-color: $color-great;
}
.badge-state-default {
background-color: $secondary;
background-color: $secondary;
}
.badge-state-success, td.badge-state-success {
background-color: $color-great;
background-color: $color-great;
}
$lighten-alert: 35%;
.alert-critical {
background-color: lighten($color-critical, $lighten-alert);
color: $color-critical;
border-color: $color-critical;
background-color: lighten($color-critical, $lighten-alert);
color: $color-critical;
border-color: $color-critical;
}
.alert-error {
background-color: lighten($color-error, $lighten-alert);
color: $color-error;
border-color: $color-error;
background-color: lighten($color-error, $lighten-alert);
color: $color-error;
border-color: $color-error;
}
.alert-warning {
background-color: lighten($color-warning, $lighten-alert);
color: $color-warning;
border-color: $color-warning;
background-color: lighten($color-warning, $lighten-alert);
color: $color-warning;
border-color: $color-warning;
}
.alert-good {
background-color: lighten($color-good, $lighten-alert);
color: $color-good;
border-color: $color-good;
background-color: lighten($color-good, $lighten-alert);
color: $color-good;
border-color: $color-good;
}
.alert-best {
background-color: lighten($color-best, $lighten-alert);
color: $color-best;
border-color: $color-best;
background-color: lighten($color-best, $lighten-alert);
color: $color-best;
border-color: $color-best;
}
.alert-great {
background-color: lighten($color-great, $lighten-alert);
color: $color-great;
border-color: $color-great;
background-color: lighten($color-great, $lighten-alert);
color: $color-great;
border-color: $color-great;
}
table.scoring img {
width: 30px;
width: 30px;
}
#about, #help {
margin-bottom: 20px;
margin-bottom: 20px;
}
#about, #help .scoring {
p {
font-size: 1.25em;
}
p {
font-size: 1.25em;
}
}
#donorbox {
background: #2d81c5 url("../images/donorbox.png") no-repeat 18px center;
color: #fff;
text-decoration: none;
font-family: Verdana, sans-serif;
display: inline-block;
font-size: 16px;
padding: 13px 17px 13px 56px;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
box-shadow: 0 1px 0 0 #1f5a89;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
background: #2d81c5 url("../images/donorbox.png") no-repeat 18px center;
color: #fff;
text-decoration: none;
font-family: Verdana, sans-serif;
display: inline-block;
font-size: 16px;
padding: 13px 17px 13px 56px;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
box-shadow: 0 1px 0 0 #1f5a89;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
}
#liberapay, #donorbox, #paypal {
img {
height: 50px;
max-width: 140px;
}
img {
height: 50px;
max-width: 140px;
}
}
/** Form part */
.form-control,
.form-select {
border-color: $dark;
}
.input-danger {
@extend .form-control;
border-color: $danger;
}
label {
font-weight: bold;
color: $dark;
}
label.required:after {
font-weight: normal;
content: '*';
color: red;
}
fieldset {
border: 1px solid $dark;
border-radius: .5rem;
}
legend {
border-radius: .5rem;
padding: .2rem .4rem;
}
/** Navigation tabs */
.tab-content {
border: 1px solid $nav-pills-link-active-bg;
border-bottom-left-radius: .3rem;
border-bottom-right-radius: .3rem;
}
.nav-pills .nav-link {
border-radius: .3rem;
font-weight: bold;
}
.nav-pills .nav-link.active {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
font-weight: bolder;
}
/** Chart CSS */
.cumulative-datas {
position: relative;
display: flex;
flex-direction: row;
width: 100%;
&-content {
position: absolute;
height: 2rem;
}
position: relative;
display: flex;
flex-direction: row;
width: 100%;
&-content {
position: absolute;
height: 2rem;
}
}
.cumulative-data {
height: 2rem;
line-height: 2rem;
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
margin-right: .1rem;
padding-left: .3rem;
height: 2rem;
line-height: 2rem;
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
margin-right: .1rem;
padding-left: .3rem;
}

44
app/javascript/js/stats/ciphers.js

@ -0,0 +1,44 @@
document.addEventListener("DOMContentLoaded", () => {
if (document.getElementById("pills-tab")) {
let background = [
'#d9534f', // bad
'#5cb85c' // good
]
for (const service of ["https", "smtp", "tls", "xmpp"]) {
const name = service.replace(/^\w/, c => c.toUpperCase())
const canvas = document.getElementById(`ciphers${name}Chart`).getContext('2d')
const chart = new Chart(canvas, {
type: 'doughnut', options: {
interaction: {
intersect: false, mode: 'dataset',
},
animations: {
tension: {
duration: 100,
easing: 'linear',
from: 1,
to: 0,
loop: false
}
},
}
})
fetch(`/statistics/${service}/ciphers.json`).then((response) => {
if (response.status === 200) {
response.json().then((data) => {
console.info(data)
const labels = Object.keys(data)
const dataset = Object.values(data)
chart.data.labels = labels
chart.data.datasets = [{
label: 'Number of request', data: dataset, backgroundColor: background
}]
chart.update()
})
}
})
}
}
})

46
app/javascript/js/stats/tls.js

@ -0,0 +1,46 @@
document.addEventListener("DOMContentLoaded", () => {
if (document.getElementById("pills-tab")) {
let background = [
'#d9534f', // ssl
'#beb052', // tls
'#beb052', // tls
'#5cb85c' // tls1_2_only
]
for (const service of ["https", "smtp", "tls", "xmpp"]) {
const name = service.replace(/^\w/, c => c.toUpperCase())
const canvas = document.getElementById(`tls${name}Chart`).getContext('2d')
const chart = new Chart(canvas, {
type: 'doughnut', options: {
interaction: {
intersect: false, mode: 'dataset',
},
animations: {
tension: {
duration: 100,
easing: 'linear',
from: 1,
to: 0,
loop: false
}
},
}
})
fetch(`/statistics/${service}/tls.json`).then((response) => {
if (response.status === 200) {
response.json().then((data) => {
console.info(data)
const labels = Object.keys(data)
const dataset = Object.values(data)
chart.data.labels = labels
chart.data.datasets = [{
label: 'Number of request', data: dataset, backgroundColor: background
}]
chart.update()
})
}
})
}
}
})

4
app/javascript/packs/application.js

@ -1,8 +1,10 @@
import 'css/application'
import 'bootstrap'
import Chart from 'chart.js/auto'
import ChartDataLabels from 'chartjs-plugin-datalabels'
global.Chart = Chart
Chart.register(ChartDataLabels)
import 'js/stats/index'
import 'js/stats/ciphers'
import 'js/stats/tls'

2
app/views/check/processing.html.erb

@ -4,7 +4,7 @@
<div id="check" class="container">
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<h1>
<h1 class="text-dark">
<i class="fa fa-spinner fa-pulse"></i>
[<%= self.type.to_s.upcase %>] <%= t 'Currently analysing %{host}', host: @host %>
</h1>

6
app/views/check/show.html.erb

@ -1,7 +1,7 @@
<div class="container">
<div class="row">
<div class="col-sm-11">
<h1>
<h1 class="text-dark">
[<%= self.type.to_s.upcase %>] <%= @host %>
<span class="small">(<%= l @analysis.updated_at %>)</span>
</h1>
@ -17,7 +17,7 @@
if host.error %>
<div class="row">
<div class="col-sm-12">
<h2>
<h2 class="text-dark">
<%= rank_label :X %>
<%= host[:ip] %> : <%= host[:port] %>
<span class="small">(<%= host[:hostname] %>)</span>
@ -29,7 +29,7 @@
<% next; end %>
<div class="row">
<div class="col-sm-12">
<h2>
<h2 class="text-dark">
<%= rank_label host[:grade].to_sym %>
<%= host[:ip] %> : <%= host[:port] %>
<span class="small">(<%= host[:hostname] %>)</span></h2>

2
app/views/site/index.html.erb

@ -11,7 +11,7 @@
</div>
<div class="col-auto input-group-lg">
<%= submit_tag t('Test me!'), class: %i[btn btn-outline-primary] %>
<%= submit_tag t('Test me!'), class: %i[btn btn-outline-dark] %>
</div>
</div>
<% end %>

2
app/views/site/suite_index.html.erb

@ -7,7 +7,7 @@
</div>
<div class="col-auto input-group-lg">
<%= submit_tag t('Test me!'), class: %i[btn btn-outline-primary] %>
<%= submit_tag t('Test me!'), class: %i[btn btn-outline-dark] %>
</div>
</div>
<% end %>

2
app/views/ssh/index.html.erb

@ -14,7 +14,7 @@
</div>
<div class="col-auto input-group-lg">
<%= submit_tag t('Test me!'), class: %i[btn btn-outline-primary] %>
<%= submit_tag t('Test me!'), class: %i[btn btn-outline-dark] %>
</div>
</div>
<% end %>

156
app/views/statistics/index.html.erb

@ -1,10 +1,6 @@
<!-- <div class="col-4">-->
<!-- <h2>Requests per service</h2>-->
<!-- <canvas id="servicesChart" aria-label="Pie chart for number of requests per service" role="img"></canvas>-->
<!-- </div>-->
<% colors = {
"A+" => '#5cb85c',
"good" => '#5cb85c',
"A" => '#5cb85c',
"B+" => '#8db457',
"B" => '#8db457',
@ -13,69 +9,109 @@
"D" => '#6c757d',
"E" => '#f0ad4e',
"F" => '#e4804e',
"G" => '#d9534f' } %>
"G" => '#d9534f',
"bad" => '#d9534f' } %>
<ul class="nav nav-pills nav-fill" id="pills-tab" role="tab-list">
<% %i[https smtp tls xmpp].each do |service| %>
<li class="nav-item" role="presentation">
<button class="nav-link<%= " active btn-dark" if service.to_s == "https" %>"
id="nav-<%= service %>-pill" data-bs-toggle="pill"
data-bs-target="#nav-<%= service %>" type="button" role="pill" aria-controls="nav-<%= service %>" aria-selected="true">
<%= service.to_s.upcase %>
</button>
</li>
<% end %>
</ul>
<% %i[https smtp tls xmpp].each do |service| %>
<div>
<h2>Grades for service <%= service.to_s.upcase %></h2>
<div class="tab-content p-2" id="nav-pillsContent">
<% %i[https smtp tls xmpp].each do |service| %>
<div class="tab-pane fade show <%= "active" if service.to_s == "https" %>" id="nav-<%= service %>" role="tabpanel" aria-labelledby="nav-<%= service %>-pill">
<h2>Grades for service <%= service.to_s.upcase %></h2>
<% grades = Stat.where(name: "grades_for_#{service}").order(date: :desc).first
total = grades.data.collect { _2 }.sum %>
<% grades = Stat.where(name: "grades_for_#{service}").order(date: :desc).first
total = grades.data.collect { _2 }.sum %>
Over <%= total %> URL tested with a grade.<br>
<p>Over <%= total %> URL tested with a grade.</p>
<!-- SVG solution -->
<em>SVG solution</em>
<div class="row">
<svg width="100%" height="45" role="img" xmlns="http://www.w3.org/2000/svg" aria-labelledby="<%= "title-grades-#{service} description-grades-#{service}" %>">
<title id="<%= "title-grades-#{service}" %>">Last grades for service <%= service %></title>
<description id="<%= "description-grades-#{service}" %>">
This graphic represents the percentage of different grades obtained for the last analysis of
service <%= service %> requested. The obtained grades are:
<% grades.data.sort_by(&:first).each do |grade, number| %>
<% unless %w(T V).include?(grade)
percent = (number.to_f / total.to_f) * 100.0 %>
<%= "#{grade}: #{percent.round(2)} (#{number} requests)" %>;
<% end
end %>
Exotic grades like T or V are excluded from this graphic.
</description>
<% y = 0
x = 0
<div class="cumulative-datas">
<% left = 0
percent_total = 100
grades.data.sort_by(&:first).each do |grade, number|
%>
<% unless %w(T V).include?(grade) %>
<% percent = (number.to_f / total.to_f) * 100.0
color = colors[grade] %>
<rect x="<%= x %>%" y="<%= y %>" width="<%= "#{percent.round}%" %>"
height="40" style="<%= "fill:#{color};" %>"
data-service="<%= service %>"
data-grade="<%= grade %>"
data-percent="<%= "#{percent}%" %>"
data-number="<%= number %>"
/>
<text x="<%= x + 0.4 %>%" y="25" style="fill:black;"><%= "#{grade}: #{percent.round}% (#{number})" %></text>
<% x += percent.round %>
<% end
end %>
</svg>
</div>
<% unless %w(T V).include?(grade)
percent = (number.to_f / total.to_f) * 100.0
color = colors[grade]
if percent > 1 %>
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: <%= color %>;">
<%= "#{grade}: #{percent.round}% (#{number})" %>
</div>
<% left += percent.round
end
end
end
percent = percent_total - left %>
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: gray;">
<%= "other: #{percent.round}%" %>
</div>
</div>
<h2>Ciphers for service <%= service.to_s.upcase %></h2>
<!-- DIV solution -->
<em>DIV solution</em>
<div class="cumulative-datas">
<% left = 0
grades.data.sort_by(&:first).each do |grade, number| %>
<% unless %w(T V).include?(grade)
percent = (number.to_f / total.to_f) * 100.0
color = colors[grade] %>
<% ciphers = Stat.where(name: "ciphers_for_#{service}").order(date: :desc).first
total = ciphers.data.collect { _2 }.sum
%>
<p>Over <%= total %> URL tested with a cipher.</p>
<div class="cumulative-datas">
<% left = 0
percent_total = 100
ciphers.data.sort_by(&:first).each do |grade, number|
%>
<%
percent = (number.to_f / total.to_f) * 100.0
color = colors[grade] %>
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: <%= color %>;">
<%= "#{grade}: #{percent.round}% (#{number})" %>
<%= "#{grade.capitalize}: #{percent.round}% (#{number})" %>
</div>
<% left += percent.round
end
percent = percent_total - left
if percent > 0 %>
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: gray;">
<%= "Other: #{percent.round}%" %>
</div>
<% end %>
</div>
<h2>TLS for service <%= service.to_s.upcase %></h2>
<% tls = Stat.where(name: "tls_for_#{service}").order(date: :desc).first
total = tls.data.collect { _2 }.sum
%>
<p>Over <%= total %> URL tested with TLS.</p>
<div class="cumulative-datas">
<% left = 0
percent_total = 100
tls.data.sort_by(&:first).each do |grade, number|
%>
<%
percent = (number.to_f / total.to_f) * 100.0
color = colors[grade] %>
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: gray;">
<%= "#{grade.capitalize}: #{percent.round}% (#{number})" %>
</div>
<% left += percent.round
end
percent = percent_total - left
if percent > 0 %>
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: gray;">
<%= "Other: #{percent.round}%" %>
</div>
<% left += percent.round %>
<% end
end %>
<% end %>
</div>
</div>
</div>
<% end %>
<% end %>
</div>

2
app/views/tls/index.html.erb

@ -13,7 +13,7 @@
</div>
<div class="col-auto input-group-lg">
<%= submit_tag t('Test me!'), class: %i[btn btn-outline-primary] %>
<%= submit_tag t('Test me!'), class: %i[btn btn-outline-dark] %>
</div>
</div>
<% end %>

9
config/routes.rb

@ -35,10 +35,15 @@ Rails.application.routes.draw do
root 'site#index'
post '/' => 'site#check'
resources :statistics, only: %i[index show]
get 'sites' => 'site#sites'
resources :statistics, only: %i[index show] do
member do
get "ciphers", to: "statistics#ciphers"
get "tls", to: "statistics#tls"
end
end
if Rails.env.development?
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'

Loading…
Cancel
Save