tryzealot/zealot

View on GitHub
app/controllers/admin/system_info_controller.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# frozen_string_literal: true

class Admin::SystemInfoController < ApplicationController
  FILE_PERMISSIONS = {
    app: [
      'log',
      'public/backup',
      'public/uploads',
      'tmp'
    ],
    system: [
      '/tmp'
    ]
  }.freeze

  EXCLUDED_MOUNT_OPTIONS = [
    'nobrowse',
    'read-only',
    'ro',
    'noexec'
  ].freeze

  EXCLUDED_MOUNT_TYPES = [
    'autofs',
    'binfmt_misc',
    'cgroup',
    'debugfs',
    'devfs',
    'devpts',
    'devtmpfs',
    'efivarfs',
    'fuse.gvfsd-fuse',
    'fuseblk',
    'fusectl',
    'hugetlbfs',
    'mqueue',
    'proc',
    'pstore',
    'rpc_pipefs',
    'securityfs',
    'sysfs',
    'tmpfs',
    'tracefs',
    'vfat'
  ].freeze

  # GET /admin/system_info
  def index
    set_env
    set_gems
    set_server_info
    set_disk_volumes
    set_file_permissions
  end

  private

  def set_file_permissions
    @file_permissions = {
      health: true,
      permissions: []
    }

    FILE_PERMISSIONS.each do |scope, paths|
      paths.each do |path|
        real_path = scope == :system ? path : Rails.root.join(path)
        health = File.writable?(real_path)

        @file_permissions[:health] = false if !health
        @file_permissions[:permissions].push(path: real_path.to_s, health: health)
      end
    end

    @file_permissions
  end

  def set_env
    @env = ENV.sort
  end

  def set_gems
    @gems ||= Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }].sort
  end

  def set_disk_volumes
    @disks = Sys::Filesystem.mounts.each_with_object([]) do |mount, obj|
      mount_options = mount.options.split(',').map(&:strip)
      # next if (EXCLUDED_MOUNT_OPTIONS & mount_options).any?
      next if (EXCLUDED_MOUNT_TYPES & [mount.mount_type]).any?

      begin
        disk = Sys::Filesystem.stat(mount.mount_point)
        next if obj.any? { |i| i[:mount_path] == disk.path }
        next if disk.bytes_total.zero?

        percent = percent(disk.bytes_used, disk.bytes_total)
        obj.push(
          bytes_total: disk.bytes_total,
          bytes_used: disk.bytes_used,
          mount_path: disk.path&.force_encoding('UTF-8'),
          mount_options: mount.options,
          percent: percent,
          color: progress_color(percent)
        )
      rescue Sys::Filesystem::Error
        next
      end
    end
  end

  def set_server_info
    require 'etc'

    @server = {
      os_info: Etc.uname.values.join(' '),
      pg_version: pg_version,
      ruby_version: RUBY_DESCRIPTION,
      zealot_vcs_ref: Setting.vcs_ref,
      build_date: Setting.build_date,
      cpu: cpu&.length,
      memory: memory,
      diskspace: diskspace,
      booted_at: Rails.application.config.booted_at
    }
  end

  def cpu
    @cpu ||= Vmstat.cpu
  rescue
    @cpu = nil
  end

  def memory
    return @memory if @memory

    memory = Vmstat.memory
    percent = percent(memory.active_bytes, memory.total_bytes).round(0)
    @memory = {
      used: memory.active_bytes,
      total: memory.total_bytes,
      percent: percent,
      color: progress_color(percent)
    }
  rescue
    @memory = nil
  end

  def diskspace
    return @diskspace if @diskspace

    disk = Sys::Filesystem.stat(Rails.root)
    percent = percent(disk.bytes_used, disk.bytes_total).round(0)
    @diskspace ||= {
      used: disk.bytes_used,
      total: disk.bytes_total,
      percent: percent,
      color: progress_color(percent)
    }
  rescue
    @diskspace = nil
  end

  def pg_version
    return false unless HealthCheck::Utils.get_database_version.present?

    version = ActiveRecord::Base.connection.select_value("SELECT version()")
    version.match(/^PostgreSQL\s((\d+[.]?)+)\s*/).try(:[], 1)
  end

  def percent(value, n)
    value.to_f / n.to_f * 100.0
  end

  def progress_color(percent)
    case percent.to_i
    when 0..60
      'btn-success'
    when 61..80
      'btn-warning'
    else
      'btn-danger'
    end
  end
end