# frozen_string_literal: true # Estadísticas del sitio class StatsController < ApplicationController include Pundit include ActionView::Helpers::DateHelper before_action :authenticate_usuarie! before_action :authorize_stats EXTRA_OPTIONS = { builds: {}, space_used: { bytes: true }, build_time: {} }.freeze # XXX: Permitir a Chart.js inyectar su propio CSS content_security_policy only: :index do |policy| policy.style_src :self, :unsafe_inline policy.script_src :self, :unsafe_inline end def index @chart_params = { interval: interval } hostnames last_stat chart_options normalized_urls end # Genera un gráfico de visitas por dominio asociado a este sitio def host return unless stale? [last_stat, hostnames, interval] stats = Rollup.where_dimensions(host: hostnames).multi_series('host', interval: interval).tap do |series| series.each do |serie| serie[:name] = serie.dig(:dimensions, 'host') serie[:data].transform_values! do |value| value * nodes end end end render json: stats end def resources return unless stale? [last_stat, interval, resource] options = { interval: interval, dimensions: { deploy_id: @site.deploys.where(type: 'DeployLocal').pluck(:id).first } } render json: Rollup.series(resource, **options) end def uris return unless stale? [last_stat, hostnames, interval, normalized_urls] options = { host: hostnames, uri: normalized_paths } stats = Rollup.where_dimensions(**options).multi_series('host|uri', interval: interval).tap do |series| series.each do |serie| serie[:name] = serie.dig(:dimensions).slice('host', 'uri').values.join.sub('/index.html', '/') serie[:data].transform_values! do |value| value * nodes end end end render json: stats end private def last_stat @last_stat ||= Stat.last end def authorize_stats @site = find_site authorize SiteStat.new(@site) end # TODO: Eliminar cuando mergeemos referer-origin def hostnames @hostnames ||= [@site.hostname, @site.alternative_hostnames].flatten end # Normalizar las URLs # # @return [Array] def normalized_urls @normalized_urls ||= params.permit(:urls).try(:[], :urls)&.split("\n")&.map(&:strip)&.select(&:present?)&.select do |uri| uri.start_with? 'https://' end&.map do |u| # XXX: Eliminar # @see {https://0xacab.org/sutty/containers/nginx/-/merge_requests/1} next u unless u.end_with? '/' "#{u}index.html" end&.uniq || [] end def normalized_paths @normalized_paths ||= normalized_urls.map do |u| "/#{u.split('/', 4).last}" end end # Opciones por defecto para los gráficos. # # La invitación a volver dentro de X tiempo es para dar un estimado de # cuándo habrá información disponible, porque Rollup genera intervalos # completos (¿aunque dice que no?) # # La diferencia se calcula sumando el intervalo a la hora de última # toma de estadísticas y restando el tiempo que pasó desde ese # momento. def chart_options time = last_stat.created_at + 1.try(interval) please_return_at = { please_return_at: distance_of_time_in_words(Time.now, time) } @chart_options ||= { locale: I18n.locale, empty: I18n.t('stats.index.empty', **please_return_at), loading: I18n.t('stats.index.loading'), html: %(