summaryrefslogtreecommitdiff
path: root/lib/dashing.rb
blob: 63fdc3791b26477bbe90c26c06a333a1aefa8262 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
require 'sinatra'
require 'sprockets'
require 'sinatra/content_for'
require 'rufus/scheduler'
require 'coffee-script'
require 'sass'
require 'json'
require 'yaml'

SCHEDULER = Rufus::Scheduler.start_new

set :root, Dir.pwd

set :sprockets,     Sprockets::Environment.new(settings.root)
set :assets_prefix, '/assets'
set :digest_assets, false
['assets/javascripts', 'assets/stylesheets', 'assets/fonts', 'assets/images', 'widgets', File.expand_path('../../javascripts', __FILE__)]. each do |path|
  settings.sprockets.append_path path
end

set server: 'thin', connections: [], history_file: 'tmp/history.yml'

# Persist history in tmp file at exit
at_exit do
  File.open(settings.history_file, 'w') do |f|
    f.puts settings.history.to_yaml
  end
end

if File.exists?(settings.history_file)
  set history: YAML.load_file(settings.history_file)
else
  set history: {}
end

set :public_folder, File.join(settings.root, 'public')
set :views, File.join(settings.root, 'dashboards')
set :default_dashboard, nil
set :auth_token, nil

helpers Sinatra::ContentFor
helpers do
  def protected!
    # override with auth logic
  end
end

get '/events', provides: 'text/event-stream' do
  protected!
  response.headers['X-Accel-Buffering'] = 'no' # Disable buffering for nginx
  stream :keep_open do |out|
    settings.connections << out
    out << latest_events
    out.callback { settings.connections.delete(out) }
  end
end

get '/' do
  begin
  redirect "/" + (settings.default_dashboard || first_dashboard).to_s
  rescue NoMethodError => e
    raise Exception.new("There are no dashboards in your dashboard directory.")
  end
end

get '/:dashboard' do
  protected!
  if File.exist? File.join(settings.views, "#{params[:dashboard]}.erb")
    erb params[:dashboard].to_sym
  else
    halt 404
  end
end

get '/views/:widget?.html' do
  protected!
  widget = params[:widget]
  send_file File.join(settings.root, 'widgets', widget, "#{widget}.html")
end

post '/widgets/:id' do
  request.body.rewind
  body =  JSON.parse(request.body.read)
  auth_token = body.delete("auth_token")
  if !settings.auth_token || settings.auth_token == auth_token
    send_event(params['id'], body)
    204 # response without entity body
  else
    status 401
    "Invalid API key\n"
  end
end

not_found do
  send_file File.join(settings.public_folder, '404.html')
end

def development?
  ENV['RACK_ENV'] == 'development'
end

def production?
  ENV['RACK_ENV'] == 'production'
end

def send_event(id, body)
  body[:id] = id
  body[:updatedAt] ||= Time.now.to_i
  event = format_event(body.to_json)
  Sinatra::Application.settings.history[id] = event
  Sinatra::Application.settings.connections.each { |out| out << event }
end

def format_event(body)
  "data: #{body}\n\n"
end

def latest_events
  settings.history.inject("") do |str, (id, body)|
    str << body
  end
end

def first_dashboard
  files = Dir[File.join(settings.views, '*.erb')].collect { |f| f.match(/(\w*).erb/)[1] }
  files -= ['layout']
  files.first
end

Dir[File.join(settings.root, 'lib', '**', '*.rb')].each {|file| require file }
{}.to_json # Forces your json codec to initialize (in the event that it is lazily loaded). Does this before job threads start.

job_path = ENV["JOB_PATH"] || 'jobs'
files = Dir[File.join(settings.root, job_path, '/*.rb')]
files.each { |job| require(job) }