|
| 1 | +require "./plusone/*" |
| 2 | +require "kemal" |
| 3 | +require "openssl/hmac" |
| 4 | +require "redis" |
| 5 | +require "json" |
| 6 | +require "uri" |
| 7 | +require "http/params" |
| 8 | + |
| 9 | +Kemal.config.port = ENV["PORT"].to_i |
| 10 | + |
| 11 | +uri = URI.parse(ENV["REDIS_URL"]) |
| 12 | +$redis = Redis.new(uri.host.to_s, uri.port.to_s.to_i, nil, uri.password) |
| 13 | + |
| 14 | +Plusone::Ping.start_pinging_redis |
| 15 | + |
| 16 | +logger = Kemal::LogHandler.new |
| 17 | + |
| 18 | +module Plusone |
| 19 | + def self.set_headers(context) |
| 20 | + # Caching headers - see https://github.com/github/markup/issues/224#issuecomment-37663375 |
| 21 | + context.response.headers.add "Cache-Control", "no-cache, no-store, private, must-revalidate, max-age=0, max-stale=0, post-check=0, pre-check=0" |
| 22 | + context.response.headers.add "Pragma", "no-cache" |
| 23 | + context.response.headers.add "Expires", "0" |
| 24 | + context.response.content_type = "image/svg+xml" |
| 25 | + end |
| 26 | + |
| 27 | + def self.get_svg(text) |
| 28 | + color = "green" |
| 29 | + w = 100 |
| 30 | + s_x = w * 0.7 |
| 31 | + |
| 32 | + "<svg xmlns='http://www.w3.org/2000/svg' width='#{w}' height='20'><linearGradient id='a' x2='0' y2='100%'><stop offset='0' stop-color='#bbb' stop-opacity='.1'/><stop offset='1' stop-opacity='.1'/></linearGradient><rect rx='3' width='#{w}' height='20' fill='#555'/><rect rx='3' x='37' width='#{w - 37}' height='20' fill='#{color}'/><path fill='#{color}' d='M37 0h4v20h-4z'/><rect rx='3' width='#{w}' height='20' fill='url(#a)'/><g fill='#fff' text-anchor='middle' font-family='Geneva,sans-serif' font-size='11'><text x='19.5' y='15' fill='#010101' fill-opacity='.3'>build</text><text x='19.5' y='14'>+1s:</text><text x='#{s_x}' y='15' fill='#010101' fill-opacity='.3'>#{text}</text><text x='#{s_x}' y='14'>#{text}</text></g></svg>" |
| 33 | + end |
| 34 | + |
| 35 | + get "/" do |context| |
| 36 | + if Plusone::Auth.authed?(context, ENV["HTTP_USERNAME"], ENV["HTTP_PASSWORD"]) |
| 37 | + repo = context.params.fetch("repo", "") as String |
| 38 | + issue = context.params.fetch("issue", "") as String |
| 39 | + |
| 40 | + instructions = "" |
| 41 | + |
| 42 | + if repo != "" && issue != "" |
| 43 | + counter = Plusone::Counter.new(repo, issue) |
| 44 | + badge = "https://#{ENV["HEROKU_APP_NAME"]}.herokuapp.com/count.svg?repo=#{repo}&issue=#{issue}&sig=#{counter.badge_signature}" |
| 45 | + webhook = "https://#{ENV["HEROKU_APP_NAME"]}.herokuapp.com/injest?repo=#{repo}&sig=#{counter.injest_signature}" |
| 46 | + |
| 47 | + # build the actual badge |
| 48 | + badge = "[](https://github.com/" + repo + "/issues/" + issue + ")" |
| 49 | + |
| 50 | + instructions = <<-HTML |
| 51 | + <h2>1. Setup Webhook</h2> |
| 52 | + <p>Set this once per repo. Go to <a href="https://github.com/#{repo}/settings/hooks">your github settings</a>:</p> |
| 53 | +
|
| 54 | + <ol> |
| 55 | + <li>Set the <em>Payload URL</em> to:<br /><code>#{webhook}</code></li> |
| 56 | + <li>Then select "Let me select individual events."</li> |
| 57 | + <li>Then check "Issues" & "Issue comment"</li> |
| 58 | + </ol> |
| 59 | +
|
| 60 | + <h2>2. Get Badge</h2> |
| 61 | + <p>Paste the following code in to your issue:</p> |
| 62 | +
|
| 63 | + <code>#{badge}</code> |
| 64 | + HTML |
| 65 | + end |
| 66 | + |
| 67 | + template = <<-HTML |
| 68 | + <!DOCTYPE html> |
| 69 | + <html> |
| 70 | + <head> |
| 71 | + <meta charset="UTF-8"> |
| 72 | + <title>+1</title> |
| 73 | + <style type="text/css"> |
| 74 | + body { font-family:helvetica,arial;color:#888;margin:2em} |
| 75 | + ol li { margin-bottom: 1em; } |
| 76 | + code { margin: 0.5em; padding: 0.5em; background-color: #eee; display: inline-block; } |
| 77 | + </style> |
| 78 | + </head> |
| 79 | + <body> |
| 80 | + <h1>Hello.</h1> |
| 81 | + <p>Enter your repo name and issue number to get started.</p> |
| 82 | +
|
| 83 | + <form method="get"> |
| 84 | + <input type="text" name="repo" value="#{repo}" placeholder="ukd1/plusone" /> |
| 85 | + <input type="number" name="issue" value="#{issue}" placeholder="1" /> |
| 86 | + <input type="submit" value="Get URLs" /> |
| 87 | + </form> |
| 88 | +
|
| 89 | + #{instructions} |
| 90 | + </body> |
| 91 | + </html> |
| 92 | + HTML |
| 93 | + |
| 94 | + template |
| 95 | + end |
| 96 | + end |
| 97 | + |
| 98 | + post "/injest" do |context| |
| 99 | + if context.params.fetch("repo", false) && context.params.fetch("sig", false) |
| 100 | + sig = context.params.fetch("sig").to_s |
| 101 | + counter = Plusone::Counter.new(context.params.fetch("repo").to_s) |
| 102 | + |
| 103 | + json = JSON.parse(context.request.body.to_s) |
| 104 | + if counter.injest_signature == sig |
| 105 | + if json["action"].to_s == "created" && json["comment"]["body"].to_s.includes?("+1") |
| 106 | + counter.incr(json["comment"]["user"]["id"]) |
| 107 | + elsif json["action"].to_s == "created" |
| 108 | + logger.write("Comment doesn't have +1: #{json["comment"]["body"]}") |
| 109 | + else |
| 110 | + logger.write("Ignoring useless events: #{json.inspect}") |
| 111 | + end |
| 112 | + else |
| 113 | + logger.write("Invalid signature, please copy the URL again.") |
| 114 | + end |
| 115 | + else |
| 116 | + logger.write("Error missing parameters, please copy the URL again.") |
| 117 | + end |
| 118 | + end |
| 119 | + |
| 120 | + get "/test.svg" do |context| |
| 121 | + set_headers(context) |
| 122 | + count = context.params.fetch("count") as String |
| 123 | + get_svg(count) |
| 124 | + end |
| 125 | + |
| 126 | + get "/count.svg" do |context| |
| 127 | + set_headers(context) |
| 128 | + |
| 129 | + if context.params.fetch("repo", false) && context.params.fetch("issue", false) && context.params.fetch("sig", false) |
| 130 | + counter = Plusone::Counter.new(context.params.fetch("repo").to_s, context.params.fetch("issue").to_s) |
| 131 | + |
| 132 | + if counter.badge_signature == context.params.fetch("sig", "") |
| 133 | + get_svg(counter.count) |
| 134 | + else |
| 135 | + get_svg("Bad sig") |
| 136 | + end |
| 137 | + else |
| 138 | + get_svg("Bad url") |
| 139 | + end |
| 140 | + end |
| 141 | +end |
0 commit comments