require 'sinatra/base' require 'sinatra/json' require 'sinatra/url_for' require 'tilt/haml' require 'sass' require 'pp' require 'oxidized/web/mig' require 'htmlentities' require 'charlock_holmes' module Oxidized module API class WebApp < Sinatra::Base helpers Sinatra::UrlForHelper set :public_folder, Proc.new { File.join(root, 'public') } get '/' do redirect url_for('/nodes') end get '/nodes/:filter/:value.?:format?' do @data = nodes.list.select do |node| if node[params[:filter].to_sym] == params[:value] versions = nodes.version node[:name], node[:group] node[:status] = 'never' node[:time] = 'never' node[:mtime] = 'unknown' node[:group] = 'default' unless node[:group] if node[:last] node[:status] = node[:last][:status] node[:time] = node[:last][:end] end if versions.is_a? Array node[:mtime] = versions.first[:date] end node end end out :nodes end get '/nodes.?:format?' do @data = nodes.list.map do |node| versions = nodes.version node[:name], node[:group] node[:status] = 'never' node[:time] = 'never' node[:mtime] = 'unknown' node[:group] = 'default' unless node[:group] if node[:last] node[:status] = node[:last][:status] node[:time] = node[:last][:end] end if versions.is_a? Array and (not versions.empty?) node[:mtime] = versions.first[:date] end node end out :nodes end post '/nodes/conf_search.?:format?' do @to_research = Regexp.new params[:search_in_conf_textbox] nodes_list = nodes.list.map @nodes_match = [] nodes_list.each do |n| node, @json = route_parse n[:name] config = nodes.fetch node, n[:group] if config[@to_research] @nodes_match.push({ node: n[:name], full_name: n[:full_name] }) end end @data = @nodes_match out :conf_search end get '/nodes/stats.?:format?' do @data = {} nodes.each do |node| @data[node.name] = node.stats end out :stats end get '/reload.?:format?' do nodes.load @data = 'reloaded list of nodes' out end get '/node/fetch/:node' do begin node, @json = route_parse :node @data = nodes.fetch node, nil rescue NodeNotFound => error @data = error.message end out :text end get '/node/fetch/:group/:node' do node, @json = route_parse :node @data = nodes.fetch node, params[:group] out :text end get '/node/next/?:group?/:node' do node, @json = route_parse :node begin nodes.next node rescue NodeNotFound end redirect url_for('/nodes') unless @json @data = 'ok' out end # use this to attach author/email/message to commit put '/node/next/?:group?/:node' do node, @json = route_parse :node opt = JSON.load request.body.read nodes.next node, opt redirect url_for('/nodes') unless @json @data = 'ok' out end get '/node/show/:node' do node, @json = route_parse :node @data = nodes.show node out :node end # redirect to the web page for rancid - oxidized migration get '/migration' do out :migration end # get the files send post '/migration' do number = params[:number].to_i cloginrc_file = params['cloginrc'][:tempfile] path_new_file = params['path_new_file'] router_db_files = Array.new i = 1 while i <= number do router_db_files.push({ file: params["file#{i}"][:tempfile], group: params["group#{i}"] }) i = i + 1 end migration = Mig.new(router_db_files, cloginrc_file, path_new_file) migration.go_rancid_migration redirect url_for('//nodes') end get '/css/*.css' do sass "sass/#{params[:splat].first}".to_sym end # show the lists of versions for a node get '/node/version.?:format?' do @data = nil @group = nil @node = nil node_full = params[:node_full] if node_full.include? '/' node_full = node_full.split('/') @group = node_full[0] @node = node_full[1] @data = nodes.version @node, @group else @node = node_full @data = nodes.version @node, nil end out :versions end # show the blob of a version get '/node/version/view.?:format?' do node, @json = route_parse :node @info = { node: node, group: params[:group], oid: params[:oid], date: params[:date], num: params[:num] } the_data = nodes.get_version node, @info[:group], @info[:oid] detection = ::CharlockHolmes::EncodingDetector.detect(the_data) utf8_encoded_content = ::CharlockHolmes::Converter.convert the_data, detection[:encoding], 'UTF-8' @data = HTMLEntities.new.encode(utf8_encoded_content) out :version end # show diffs between 2 version get '/node/version/diffs' do node, @json = route_parse :node @data = nil @info = {node: node, group: params[:group], oid: params[:oid], date: params[:date], num: params[:num], num2: (params[:num].to_i - 1)} group = nil if @info[:group] != '' group = @info[:group] end @oids_dates = nodes.version node, group if params[:oid2] @info[:oid2] = params[:oid2] oid2 = nil num = @oids_dates.count + 1 @oids_dates.each do |x| num -= 1 if x[:oid].to_s == params[:oid2] oid2 = x[:oid] @info[:num2] = num break end end @data = nodes.get_diff node, @info[:group], @info[:oid], oid2 else @data = nodes.get_diff node, @info[:group], @info[:oid], nil end @stat = ['null', 'null'] if @data != 'no diffs' && @data != nil @stat = @data[:stat] @data = @data[:patch] else @data = 'no available' end @diff = diff_view @data out :diffs end # used for diff between 2 distant commit post '/node/version/diffs' do redirect url_for("/node/version/diffs?node=#{params[:node]}&group=#{params[:group]}&oid=#{params[:oid]}&date=#{params[:date]}&num=#{params[:num]}&oid2=#{params[:oid2]}") end private def out template = :text if @json or params[:format] == 'json' if @data.is_a?(String) json @data.lines else json @data end elsif template == :text or params[:format] == 'text' content_type :text @data else haml template, layout: true end end def nodes settings.nodes end def route_parse param json = false if param.respond_to?(:to_str) e = param.split '.' else e = params[param].split '.' end if e.last == 'json' e.pop json = true end [e.join('.'), json] end # give the time enlapsed between now and a date def time_from_now date if date # if the + is missing unless date.include? '+' date.insert(21, '+') end date = DateTime.parse date now = DateTime.now.new_offset(0) t = ((now - date) * 24 * 60 * 60).to_i mm, ss = t.divmod(60) hh, mm = mm.divmod(60) dd, hh = hh.divmod(24) if dd > 0 date = "#{dd} days #{hh} hours ago" elsif hh > 0 date = "#{hh} hours #{mm} min ago" else date = "#{mm} min #{ss} sec ago" end end date end # method the give diffs in separate view (the old and the new) as in github def diff_view diff old_diff = [] new_diff = [] detection = ::CharlockHolmes::EncodingDetector.detect(diff) utf8_encoded_content = ::CharlockHolmes::Converter.convert diff, detection[:encoding], 'UTF-8' HTMLEntities.new.encode(utf8_encoded_content).each_line do |line| if /^\+/.match(line) new_diff.push(line) elsif /^\-/.match(line) old_diff.push(line) else new_diff.push(line) old_diff.push(line) end end length_o = old_diff.count length_n = new_diff.count for i in 0..[length_o, length_n].max if i > [length_o, length_n].min break end if (/^\-.*/.match(old_diff[i])) && !(/^\+.*/.match(new_diff[i])) # tag removed latter to add color syntax insert = 'empty_line' # ugly way to avoid asymmetry if at display the line takes 2 line on the screen insert = " \n" new_diff.insert(i, insert) length_n += 1 elsif !(/^\-.*/.match(old_diff[i])) && (/^\+.*/.match(new_diff[i])) insert = 'empty_line' insert = " \n" old_diff.insert(i, insert) length_o += 1 end end { old_diff: old_diff, new_diff: new_diff } end end end end