From 4ecd1ab5c85e776aff8cf467f0a9540deb262398 Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Mon, 1 Apr 2019 21:07:09 -0700 Subject: [PATCH 01/15] Don't papertrail things that shouldn't be paper trailed. Closes #1630 --- app/models/potential_payment.rb | 1 - app/models/site_channel_details.rb | 1 - app/models/twitch_channel_details.rb | 1 - app/models/twitter_channel_details.rb | 1 - app/models/youtube_channel_details.rb | 1 - .../remove_unused_versions.rake | 19 +++++++++++++++++++ 6 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 lib/tasks/database_updates/remove_unused_versions.rake diff --git a/app/models/potential_payment.rb b/app/models/potential_payment.rb index 1945aef129..ff02319b66 100644 --- a/app/models/potential_payment.rb +++ b/app/models/potential_payment.rb @@ -2,7 +2,6 @@ class PotentialPayment < ApplicationRecord REFERRAL = "referral".freeze CONTRIBUTION = "contribution".freeze MANUAL = "manual".freeze - has_paper_trail belongs_to :payout_report belongs_to :publisher diff --git a/app/models/site_channel_details.rb b/app/models/site_channel_details.rb index 46d5a70aef..1f54355884 100644 --- a/app/models/site_channel_details.rb +++ b/app/models/site_channel_details.rb @@ -1,5 +1,4 @@ class SiteChannelDetails < BaseChannelDetails - has_paper_trail # brave_publisher_id is a normalized identifier provided by eyeshade API # It is like base domain (eTLD + left part) but may include additional diff --git a/app/models/twitch_channel_details.rb b/app/models/twitch_channel_details.rb index 0400d6c49f..09c20eccfa 100644 --- a/app/models/twitch_channel_details.rb +++ b/app/models/twitch_channel_details.rb @@ -1,5 +1,4 @@ class TwitchChannelDetails < BaseChannelDetails - has_paper_trail validate :twitch_channel_not_changed_once_initialized validates :twitch_channel_id, presence: true diff --git a/app/models/twitter_channel_details.rb b/app/models/twitter_channel_details.rb index b605fc3640..3a18460bf3 100644 --- a/app/models/twitter_channel_details.rb +++ b/app/models/twitter_channel_details.rb @@ -1,5 +1,4 @@ class TwitterChannelDetails < BaseChannelDetails - has_paper_trail validate :twitter_channel_not_changed_once_initialized validates :twitter_channel_id, presence: true diff --git a/app/models/youtube_channel_details.rb b/app/models/youtube_channel_details.rb index 95c424a4e9..4877a5b15e 100644 --- a/app/models/youtube_channel_details.rb +++ b/app/models/youtube_channel_details.rb @@ -1,5 +1,4 @@ class YoutubeChannelDetails < BaseChannelDetails - has_paper_trail validate :youtube_channel_not_changed_once_initialized validates :youtube_channel_id, presence: true diff --git a/lib/tasks/database_updates/remove_unused_versions.rake b/lib/tasks/database_updates/remove_unused_versions.rake new file mode 100644 index 0000000000..e765df6bf4 --- /dev/null +++ b/lib/tasks/database_updates/remove_unused_versions.rake @@ -0,0 +1,19 @@ +namespace :database_updates do + task :remove_unused_versions => :environment do + class LegacyVersion < ApplicationRecord ; end + class Version < ApplicationRecord ; end + puts "[#{Time.now.iso8601}] Starting migration to remove unused version details" + + LegacyVersion.where(item_type: YoutubeChannelDetails.to_s).delete_all + LegacyVersion.where(item_type: SiteChannelDetails.to_s).delete_all + LegacyVersion.where(item_type: PotentialPayment.to_s).delete_all + LegacyVersion.where(item_type: TwitchChannelDetails.to_s).delete_all + LegacyVersion.where(item_type: TwitterChannelDetails.to_s).delete_all + Version.where(item_type: YoutubeChannelDetails.to_s).delete_all + Version.where(item_type: SiteChannelDetails.to_s).delete_all + Version.where(item_type: PotentialPayment.to_s).delete_all + Version.where(item_type: TwitchChannelDetails.to_s).delete_all + Version.where(item_type: TwitterChannelDetails.to_s).delete_all + puts "[#{Time.now.iso8601}] - migration complete ✨" + end +end From 38bba0ea127f7ca27579f7840ef5597e5fbea187 Mon Sep 17 00:00:00 2001 From: Ross Moody Date: Fri, 31 May 2019 13:03:50 -0700 Subject: [PATCH 02/15] add login-button in main section (#1935) --- public/creators-landing/src/locale/en.js | 6 ++++-- .../src/sections/main-section/index.js | 15 +++++++++++++-- public/creators-landing/src/style/style.css | 8 ++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/public/creators-landing/src/locale/en.js b/public/creators-landing/src/locale/en.js index ae980fff3a..a6b14dd949 100644 --- a/public/creators-landing/src/locale/en.js +++ b/public/creators-landing/src/locale/en.js @@ -22,8 +22,10 @@ export default { Twitch: "Twitch" }, btn: { - signup: "Sign up now", - signupHref: "/sign-up" + signup: "Sign up", + signupHref: "/sign-up", + login: "Log in", + loginHref: "/log-in" } }, signin: { diff --git a/public/creators-landing/src/sections/main-section/index.js b/public/creators-landing/src/sections/main-section/index.js index b2f59d6d76..7e342edfb8 100644 --- a/public/creators-landing/src/sections/main-section/index.js +++ b/public/creators-landing/src/sections/main-section/index.js @@ -14,7 +14,7 @@ import { import { Link } from "react-router-dom"; import SignComponent from "./signComponent"; import batPill from "../../components/img/built-with-bat-pill.svg"; -import { Heading, Box, Image } from "grommet"; +import { Heading, Box, Image, Anchor } from "grommet"; import locale from "../../locale/en"; const logAction = (action, value) => { @@ -46,7 +46,7 @@ export const MainHome = () => {

{locale.main.home.subhead}

- + logAction("StartSignupClicked", "Landing")} @@ -54,6 +54,17 @@ export const MainHome = () => { + + logAction("StartSignupClicked", "Landing")} + > + diff --git a/public/creators-landing/src/style/style.css b/public/creators-landing/src/style/style.css index 0d31a8f68c..afe414534d 100644 --- a/public/creators-landing/src/style/style.css +++ b/public/creators-landing/src/style/style.css @@ -20,6 +20,14 @@ h4 { font-weight: 400 !important; } +/* Main section styling */ + +.main-btns { + align-items: center; + display: flex; + flex-wrap: wrap; +} + #nav { position: absolute; top: 0; From 7bedf73353b9bf08bb9789ca643268d72497885b Mon Sep 17 00:00:00 2001 From: Cory McDonald Date: Fri, 31 May 2019 15:04:08 -0500 Subject: [PATCH 03/15] Alert for duplicate channels (#1921) * initial commit * Add duplicates section --- app/controllers/admin/channels_controller.rb | 11 ++++ .../views/admin/publishers/Duplicates.js | 59 +++++++++++++++++++ app/models/channel.rb | 16 +++++ app/views/admin/channels/duplicates.html.slim | 15 +++++ app/views/admin/publishers/index.html.slim | 2 + app/views/admin/shared/_sidebar.html.slim | 1 + app/views/layouts/admin.html.slim | 1 + config/routes.rb | 7 ++- db/schema.rb | 13 ++-- 9 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 app/javascript/packs/views/admin/publishers/Duplicates.js create mode 100644 app/views/admin/channels/duplicates.html.slim diff --git a/app/controllers/admin/channels_controller.rb b/app/controllers/admin/channels_controller.rb index 1c6b22f883..ca1af5fc47 100644 --- a/app/controllers/admin/channels_controller.rb +++ b/app/controllers/admin/channels_controller.rb @@ -29,6 +29,17 @@ def index @channels = @channels.paginate(page: params[:page]) end + def duplicates + @channels = Channel.duplicates + + respond_to do |format| + format.html {} + format.json { + render json: @channels + } + end + end + private def sortable_columns diff --git a/app/javascript/packs/views/admin/publishers/Duplicates.js b/app/javascript/packs/views/admin/publishers/Duplicates.js new file mode 100644 index 0000000000..bf27bb5b9d --- /dev/null +++ b/app/javascript/packs/views/admin/publishers/Duplicates.js @@ -0,0 +1,59 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; + +class Duplicates extends React.Component { + constructor(props) { + super(props); + this.state = { + count: 0 + }; + } + + componentDidMount() { + const _this = this; + + fetch("/admin/channels/duplicates", { + headers: { + Accept: "application/json", + "X-CSRF-Token": document.head + .querySelector("[name=csrf-token]") + .getAttribute("content"), + "X-Requested-With": "XMLHttpRequest" + }, + method: "GET" + }) + .then(function(response) { + return response.json(); + }) + .then(function(myJson) { + _this.setState({ count: myJson.length }); + }) + .catch(error => console.error(error)); + } + + render() { + return ( + + {this.state.count > 0 && ( +
+ + There are{" "} + + {this.state.count} duplicate channel(s). + {" "} + This could impact payout. +
+ )} +
+ ); + } +} + +document.addEventListener("DOMContentLoaded", () => { + ReactDOM.render( + , + document + .getElementById("duplicates") + .appendChild(document.createElement("div")) + ); +}); diff --git a/app/models/channel.rb b/app/models/channel.rb index 4800fdc759..fc076e484d 100644 --- a/app/models/channel.rb +++ b/app/models/channel.rb @@ -147,6 +147,22 @@ def self.statistical_totals } end + def self.duplicates + entries = [ + TwitchChannelDetails.joins(:channel).where("channels.verified = true").select(:name).group(:name), + YoutubeChannelDetails.joins(:channel).where("channels.verified = true").select(:youtube_channel_id).group(:youtube_channel_id), + TwitterChannelDetails.joins(:channel).where("channels.verified = true").select(:twitter_channel_id).group(:twitter_channel_id), + SiteChannelDetails.joins(:channel).where("channels.verified = true").select(:brave_publisher_id).group(:brave_publisher_id), + VimeoChannelDetails.joins(:channel).where("channels.verified = true").select(:vimeo_channel_id).group(:vimeo_channel_id), + ] + + duplicates = entries.map do |entry| + entry.having("count(*) >1").map { |x, y| x.channel_identifier } + end + + @duplicates ||= duplicates.flatten + end + def publication_title details.publication_title end diff --git a/app/views/admin/channels/duplicates.html.slim b/app/views/admin/channels/duplicates.html.slim new file mode 100644 index 0000000000..0846230d32 --- /dev/null +++ b/app/views/admin/channels/duplicates.html.slim @@ -0,0 +1,15 @@ +div.row + div.col-sm-12 + section.panel + header.panel-heading + h4= "Duplicate Channels" + br + div.panel-body + table.display.table.table-bordered.table-striped.dynamic-table id="dynamic-table" + tr + th Channel + tbody + - @channels.each do |channel| + tr.gradeX + td + = link_to(channel, admin_publishers_path(q: channel)) diff --git a/app/views/admin/publishers/index.html.slim b/app/views/admin/publishers/index.html.slim index 4e0071a7d7..339d3e7a5c 100644 --- a/app/views/admin/publishers/index.html.slim +++ b/app/views/admin/publishers/index.html.slim @@ -1,3 +1,5 @@ +#duplicates += javascript_pack_tag 'views/admin/publishers/Duplicates' div.row div.col-sm-12 section.panel diff --git a/app/views/admin/shared/_sidebar.html.slim b/app/views/admin/shared/_sidebar.html.slim index aadf1b4324..1559128bec 100644 --- a/app/views/admin/shared/_sidebar.html.slim +++ b/app/views/admin/shared/_sidebar.html.slim @@ -26,6 +26,7 @@ aside = nav_link "Youtube", admin_channels_path(type: 'youtube') = nav_link "Transfers", admin_channel_transfers_path = nav_link "Approvals", admin_channel_approvals_path + = nav_link "Duplicates", duplicates_admin_channels_path = nav_link "Organizations", admin_organizations_path = nav_link "Partners", admin_partners_path diff --git a/app/views/layouts/admin.html.slim b/app/views/layouts/admin.html.slim index fd439efeae..99342e18c5 100644 --- a/app/views/layouts/admin.html.slim +++ b/app/views/layouts/admin.html.slim @@ -4,6 +4,7 @@ html title Publisher Admin Dashboard = stylesheet_link_tag 'admin/main' = javascript_pack_tag 'admin' + = csrf_meta_tags body data-action=params[:action] data-controller=params[:controller] section id='container' = render 'admin/shared/header' diff --git a/config/routes.rb b/config/routes.rb index aac64686fd..5ae0d955d7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -149,7 +149,12 @@ end namespace :admin do - resources :channels, only: [:index] + resources :channels, only: [:index] do + collection do + get :duplicates + end + end + resources :faq_categories, except: [:show] resources :faqs, except: [:show] resources :payout_reports, only: %i(index show create) do diff --git a/db/schema.rb b/db/schema.rb index 1c42d0b480..5de74bab9f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -13,6 +13,7 @@ ActiveRecord::Schema.define(version: 2019_05_24_205525) do # These are extensions that must be enabled in order to support this database + enable_extension "pg_stat_statements" enable_extension "plpgsql" enable_extension "uuid-ossp" @@ -135,7 +136,7 @@ t.string "name" t.string "email" t.string "verification_token" - t.boolean "verified", default: true + t.boolean "verified", default: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "sign_in_count", default: 0, null: false @@ -365,12 +366,12 @@ t.datetime "updated_at", null: false t.datetime "two_factor_prompted_at" t.boolean "visible", default: true - t.boolean "promo_enabled_2018q1", default: false t.datetime "agreed_to_tos" + t.boolean "promo_enabled_2018q1", default: false t.string "promo_token_2018q1" t.text "role", default: "publisher" - t.datetime "default_currency_confirmed_at" t.datetime "javascript_last_detected_at" + t.datetime "default_currency_confirmed_at" t.boolean "excluded_from_payout", default: false, null: false t.uuid "created_by_id" t.uuid "default_site_banner_id" @@ -385,8 +386,8 @@ create_table "sessions", id: :serial, force: :cascade do |t| t.string "session_id", null: false t.text "data" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.index ["session_id"], name: "index_sessions_on_session_id", unique: true t.index ["updated_at"], name: "index_sessions_on_updated_at" end @@ -416,8 +417,8 @@ t.string "detected_web_host" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "https_error" t.jsonb "stats", default: "{}", null: false + t.string "https_error" end create_table "totp_registrations", id: :uuid, default: -> { "uuid_generate_v4()" }, force: :cascade do |t| From db0da0e38c19b4e80a12997f07c3acdfb7bda1ad Mon Sep 17 00:00:00 2001 From: "Dan A. Lipeles" Date: Fri, 31 May 2019 20:29:15 -0400 Subject: [PATCH 04/15] =?UTF-8?q?Update=20Admin=20Charts=20=F0=9F=8F=B7=20?= =?UTF-8?q?(#1934)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Chart to Line, and fix payments labels * Allow data selection * Syntax fixes --- .../components/currentChart/CurrentChart.tsx | 2 +- .../referralsChart/ReferralsChart.tsx | 100 ++++++++++++++---- .../promo/registrations_stats_fetcher.rb | 21 ++-- 3 files changed, 92 insertions(+), 31 deletions(-) diff --git a/app/javascript/views/admin/payments/components/currentChart/CurrentChart.tsx b/app/javascript/views/admin/payments/components/currentChart/CurrentChart.tsx index 214f1291ac..c64244b36c 100644 --- a/app/javascript/views/admin/payments/components/currentChart/CurrentChart.tsx +++ b/app/javascript/views/admin/payments/components/currentChart/CurrentChart.tsx @@ -31,7 +31,7 @@ export default class CurrentChart extends React.Component< datasets: [ { backgroundColor: ["#D2D8FD", "#A0AAF8"], - data: [contributionBalance, referralBalance] + data: [referralBalance, contributionBalance] } ], labels: ["Referrals", "Contributions"] diff --git a/app/javascript/views/admin/referrals/components/referralsChart/ReferralsChart.tsx b/app/javascript/views/admin/referrals/components/referralsChart/ReferralsChart.tsx index e9dc4159bc..1f9ddf139d 100644 --- a/app/javascript/views/admin/referrals/components/referralsChart/ReferralsChart.tsx +++ b/app/javascript/views/admin/referrals/components/referralsChart/ReferralsChart.tsx @@ -10,6 +10,9 @@ interface IReferralsChartProps { interface IReferralsChartState { selectedReferralCode: any; + downloadsToggle: any; + installsToggle: any; + confirmationsToggle: any; } export default class Referrals extends React.Component< @@ -20,6 +23,9 @@ export default class Referrals extends React.Component< constructor(props) { super(props); this.state = { + confirmationsToggle: true, + downloadsToggle: true, + installsToggle: true, selectedReferralCode: 0 }; } @@ -40,6 +46,26 @@ export default class Referrals extends React.Component< this.setState({ selectedReferralCode: e.target.value }); }; + public handleDataSelect = index => { + switch (index) { + case 0: + this.setState(prevState => ({ + downloadsToggle: !prevState.downloadsToggle + })); + break; + case 1: + this.setState(prevState => ({ + installsToggle: !prevState.installsToggle + })); + break; + case 2: + this.setState(prevState => ({ + confirmationsToggle: !prevState.confirmationsToggle + })); + break; + } + }; + public createReferralsChart(referralCode) { const node = this.node; @@ -52,34 +78,51 @@ export default class Referrals extends React.Component< const installs = []; const confirmations = []; - stats.forEach(stat => { - chartLabels.push(stat.date); - downloads.push(stat.downloads); - installs.push(stat.installs); - confirmations.push(stat.confirmations); + stats.forEach((stat, index) => { + if (index < stats.length - 1) { + if (stat.date !== stats[index + 1].date) { + chartLabels.push(stat.date); + downloads.push(stat.downloads); + installs.push(stat.installs); + confirmations.push(stat.confirmations); + } + } + if (index === stats.length - 1) { + chartLabels.push(stat.date); + downloads.push(stat.downloads); + installs.push(stat.installs); + confirmations.push(stat.confirmations); + } }); + if (!this.state.downloadsToggle) { + downloads = []; + } + if (!this.state.installsToggle) { + installs = []; + } + if (!this.state.confirmationsToggle) { + confirmations = []; + } + const chartData = { datasets: [ { - backgroundColor: "#DFF3FE", - borderColor: "#DFF3FE", + backgroundColor: "#FFFFFF00", + borderColor: "#FF6384", data: downloads, - fill: true, label: "Downloads" }, { - backgroundColor: "#D2D8FD", - borderColor: "#D2D8FD", + backgroundColor: "#FFFFFF00", + borderColor: "#36A2EB", data: installs, - fill: true, label: "Installs" }, { - backgroundColor: "#A0AAF8", - borderColor: "#A0AAF8", + backgroundColor: "#FFFFFF00", + borderColor: "#9966FF", data: confirmations, - fill: true, label: "Confirmations" } ], @@ -95,8 +138,7 @@ export default class Referrals extends React.Component< scales: { yAxes: [ { - display: false, - stacked: true + display: false } ] } @@ -108,6 +150,9 @@ export default class Referrals extends React.Component< } public render() { + const confirmationsOpacity = this.state.confirmationsToggle ? 1 : 0.5; + const installsOpacity = this.state.installsToggle ? 1 : 0.5; + const downloadsOpacity = this.state.downloadsToggle ? 1 : 0.5; return (
this.handleDataSelect(2)} style={{ - backgroundColor: "#A0AAF8", + backgroundColor: "#9966FF", borderRadius: "50%", + cursor: "pointer", height: "16px", marginRight: "4px", marginTop: "4px", + opacity: confirmationsOpacity, width: "16px" }} /> @@ -181,14 +229,19 @@ export default class Referrals extends React.Component< Confirmation
-
+
this.handleDataSelect(1)} + style={{ display: "flex" }} + >
@@ -202,14 +255,19 @@ export default class Referrals extends React.Component< Installs
-
+
this.handleDataSelect(0)} + style={{ display: "flex" }} + >
diff --git a/app/services/promo/registrations_stats_fetcher.rb b/app/services/promo/registrations_stats_fetcher.rb index 613c622b52..2f8e055383 100644 --- a/app/services/promo/registrations_stats_fetcher.rb +++ b/app/services/promo/registrations_stats_fetcher.rb @@ -39,15 +39,18 @@ def perform_offline @referral_codes.each do |referral_code| events = [] (1..6).reverse_each do |i| - event = { - "referral_code" => "#{referral_code}", - PromoRegistration::RETRIEVALS => rand(50..75), - PromoRegistration::FIRST_RUNS => rand(30..50), - PromoRegistration::FINALIZED => rand(1..30), - "ymd" => "#{i.month.ago.utc.to_date}", - } - events.push(event) - stats.push(event) + (1..3).each do |j| + puts j + event = { + "referral_code" => "#{referral_code}", + PromoRegistration::RETRIEVALS => rand(50..75), + PromoRegistration::FIRST_RUNS => rand(30..50), + PromoRegistration::FINALIZED => rand(1..30), + "ymd" => "#{i.month.ago.utc.to_date}", + } + events.push(event) + stats.push(event) + end end PromoRegistration.find_by_referral_code(referral_code).update(stats: events.to_json) end From 98b960267e69c1992d54da79f5c25bf1bb4aafc1 Mon Sep 17 00:00:00 2001 From: Cory McDonald Date: Sat, 1 Jun 2019 10:50:59 -0500 Subject: [PATCH 05/15] Fix the referral charts :) (#1938) --- .../referrals/components/referralsChart/ReferralsChart.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/views/admin/referrals/components/referralsChart/ReferralsChart.tsx b/app/javascript/views/admin/referrals/components/referralsChart/ReferralsChart.tsx index 1f9ddf139d..ac664bb63b 100644 --- a/app/javascript/views/admin/referrals/components/referralsChart/ReferralsChart.tsx +++ b/app/javascript/views/admin/referrals/components/referralsChart/ReferralsChart.tsx @@ -74,9 +74,9 @@ export default class Referrals extends React.Component< } const stats = referralCode.stats; const chartLabels = []; - const downloads = []; - const installs = []; - const confirmations = []; + let downloads = []; + let installs = []; + let confirmations = []; stats.forEach((stat, index) => { if (index < stats.length - 1) { From cbc9f3baa30949c010c07dcb6f7ab66ae9957eb6 Mon Sep 17 00:00:00 2001 From: Ross Moody Date: Sat, 1 Jun 2019 11:00:25 -0700 Subject: [PATCH 06/15] Update README.md --- README.md | 197 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 112 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index eb2e57c517..658c82570f 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,50 @@ [![Build Status](https://travis-ci.org/brave-intl/publishers.svg?branch=master)](https://travis-ci.org/brave-intl/publishers) -## :wrench: Setup - -Follow these steps to setup the App for [publishers.basicattentiontoken.org](https://publishers.brave.com). This guide presumes you are using OSX and [Homebrew](https://brew.sh/). - -1. Install __Ruby 2.4.5__. For a Ruby version manager try - [rbenv](https://github.com/rbenv/rbenv). Follow the `Installation` section instructions. Once installed run `rbenv install 2.4.5`. Be sure to restart your terminal before continuing. -2. Install __Node 6.12.3__ or greater: `brew install node` -3. Install __Postgresql 9.5+__: `brew install postgresql` - - If you get the error `psql: FATAL: role “postgres” does not exist`. You'll need to create the `/usr/local/opt/postgres/bin/createuser -s postgres` -4. Install __Redis__: `brew install redis` -5. Install __Ruby__ gems: `gem install bundler foreman mailcatcher`. +# :wrench: Setup + +Follow these steps to setup the App for [creators.brave.com](https://creators.brave.com). This guide presumes you are using OSX and [Homebrew](https://brew.sh/). + +1. Install **Ruby 2.5.5**. For a Ruby version manager try + [rbenv](https://github.com/rbenv/rbenv). Follow the `Installation` section instructions and ensure your version is at least 1.1.2. Once installed run `rbenv install 2.5.5`. Be sure to restart your terminal before continuing. +2. Install **Node 6.12.3** or greater: `brew install node` +3. Install **Postgresql 9.5+**: `brew install postgresql` + + If you get the error `psql: FATAL: role “postgres” does not exist`. You'll need to create the `/usr/local/opt/postgres/bin/createuser -s postgres` + +4. Install **Redis**: `brew install redis` +5. Install **Ruby** gems: `gem install bundler foreman mailcatcher`. - [bundler](http://bundler.io/) - [foreman](https://github.com/ddollar/foreman) - [mailcatcher](https://github.com/sj26/mailcatcher) -6. Install __[Yarn](https://yarnpkg.com/en/)__ for Node dependency management: - `brew install yarn --without-node` - - __Note:__ `--without-node` avoids installing Homebrew's version of Node, which is - desirable if you are using nvm for Node version management. +6. Install **[Yarn](https://yarnpkg.com/en/)** for Node dependency management: `brew install yarn` 7. Install project dependencies - - __Ruby__ dependencies: `bundle install` - - Possible error: Nokogiri, with libxml2. Try installing a system libxml2 - with `brew install libxml2` and then - `bundle config build.nokogiri --use-system-libraries` then again - `bundle install`.,. - - __Node__ dependencies: `yarn --frozen-lockfile` -8. Install [git-secrets](https://github.com/awslabs/git-secrets). This prevents AWS keys from being committed. + + **Ruby** dependencies: `bundle install` + + **Possible errors:** + + - Nokogiri, with libxml2. Try installing a system libxml2 + with `brew install libxml2` and then + `bundle config build.nokogiri --use-system-libraries` then again `bundle install` + - Run `gem install nokogiri -v '1.10.3'` and then `bundle install` + + **Node** dependencies: `yarn --frozen-lockfile` + + Your version of Node must be v11.15.0 or earlier. For a node version manager, try [NVM](https://github.com/nvm-sh/nvm). + +8. Install [git-secrets](https://github.com/awslabs/git-secrets) with `brew install git-secrets` This prevents AWS keys from being committed. 9. (Optional) Get an `env.sh` file from another developer which contains development-mode bash env exports and `source` that file. You can start developing without this, but some functionality may be limited. -10. Install __Rails__: `gem install rails` Be sure to restart your terminal before continuing. +10. Install **Rails**: `gem install rails` + + **Be sure to restart your terminal before continuing.** + 11. Setup SSL as described below. -### HTTPS Setup +## HTTPS Setup -Local development of brave-intl uses HTTPS. This allow us to use web APIs such -as U2F in development. +Local development of brave-intl uses HTTPS. This allow us to use web APIs such as U2F in development. -If you already have a key and certificate for the `localhost` domain place them in the -`ssl/` directory: +If you already have a key and certificate for the `localhost` domain place them in the `ssl/` directory: ``` ssl/server.key @@ -55,16 +61,25 @@ When you first visit the application in a browser you may need to add an exception to trust this self-signed certificate. Sometimes this is under an "advanced" or "proceed" link. -### Run +## Run -1. Start __Postgres__ and __Redis__: `brew services start redis postgresql` +1. Start **Postgres** and **Redis**: `brew services start redis postgresql` 2. Create and initialize the database: -``` -rails db:create RAILS_ENV=development -rails db:migrate RAILS_ENV=development -``` -__Note__: If you receive a `fatal-role` error, try running `/usr/local/opt/postgres/bin/createuser -s postgres` due to being installed from `homebrew`. Further documentation is [here.](https://stackoverflow.com/questions/15301826/psql-fatal-role-postgres-does-not-exist) + ``` + rails db:create RAILS_ENV=development + rails db:migrate RAILS_ENV=development + ``` + + **Note**: If you receive a `fatal-role` error, try running `/usr/local/opt/postgres/bin/createuser -s postgres` due to being installed from `homebrew`. Further documentation is [here.](https://stackoverflow.com/questions/15301826/psql-fatal-role-postgres-does-not-exist) + + If you receive an error about Readline, try running: + + ``` + ln -s /usr/local/opt/readline/lib/libreadline.dylib /usr/local/opt/readline/lib/libreadline.7.dylib + ``` + + Issue for [further documentation](https://github.com/deivid-rodriguez/byebug/issues/289). 3. Run Rails server and async worker: `foreman start -f Procfile.dev` @@ -74,28 +89,30 @@ __Note__: If you receive a `fatal-role` error, try running `/usr/local/opt/postg 6. To view the emails sent to your inbox visit: http://localhost:1080 +--- + ## API Setups ### Google API Setup Setup a google API project: -* Login to your google account (dev), or the Brave google account (staging, production) -* Go to [https://console.developers.google.com](https://console.developers.google.com) -* Select "Create Project" then "Create" to setup a new API project -* Give the project a name such as "publishers-dev" -* Select "+ Enable APIs and Services" -* Enable "Google+ API" and "YouTube Data API v3" -* Back at the console select Credentials, then select the "OAuth consent screen" sub tab -* Fill in the details. For development you need the Product name, try "Publishers Dev (localhost)" -* Then Select "Create credentials", then "OAuth client ID" - * Application type is "Web application" - * Name is "Publishers" - * Authorized redirect URIs is `http://localhost:3000/publishers/auth/google_oauth2/callback` - * select "Create" -* Record the Client ID and Client secret and enter them in your Env variables -* Back at the console select "Create credentials" and select API key. This will be used for youtube channel stats via the data api. -* Record the API and enter it in your Env variables +- Login to your google account (dev), or the Brave google account (staging, production) +- Go to [https://console.developers.google.com](https://console.developers.google.com) +- Select "Create Project" then "Create" to setup a new API project +- Give the project a name such as "publishers-dev" +- Select "+ Enable APIs and Services" +- Enable "Google+ API" and "YouTube Data API v3" +- Back at the console select Credentials, then select the "OAuth consent screen" sub tab +- Fill in the details. For development you need the Product name, try "Publishers Dev (localhost)" +- Then Select "Create credentials", then "OAuth client ID" + - Application type is "Web application" + - Name is "Publishers" + - Authorized redirect URIs is `http://localhost:3000/publishers/auth/google_oauth2/callback` + - select "Create" +- Record the Client ID and Client secret and enter them in your Env variables +- Back at the console select "Create credentials" and select API key. This will be used for youtube channel stats via the data api. +- Record the API and enter it in your Env variables You may need to wait up to 10 minutes for the changes to propagate. @@ -105,29 +122,29 @@ These steps based on [directions at the omniauth-google-oauth2 gem](https://gith Setup a twitch API project: -* Login to your Twitch account (dev), or the Brave Twitch account (staging, production) -* Go to [https://dev.twitch.tv/dashboard](https://dev.twitch.tv/dashboard) -* Select "Get Started" for "App" -* Give the project a name such as "publishers-dev" -* Give the app a name and application category. -* Use the redirect URI `https://localhost:3000/publishers/auth/register_twitch_channel/callback` in development. -* Create a Client ID and secret, saving each of them. - * Update your env to include `TWITCH_CLIENT_ID="your-app-id"` - * Update your env to include `TWITCH_CLIENT_SECRET="your-app-secret"` -* Save the app +- Login to your Twitch account (dev), or the Brave Twitch account (staging, production) +- Go to [https://dev.twitch.tv/dashboard](https://dev.twitch.tv/dashboard) +- Select "Get Started" for "App" +- Give the project a name such as "publishers-dev" +- Give the app a name and application category. +- Use the redirect URI `https://localhost:3000/publishers/auth/register_twitch_channel/callback` in development. +- Create a Client ID and secret, saving each of them. + - Update your env to include `TWITCH_CLIENT_ID="your-app-id"` + - Update your env to include `TWITCH_CLIENT_SECRET="your-app-secret"` +- Save the app ### Twitter API Setup -* Apply for a developer account at [developer.twitter.com](https://developer.twitter.com/) -* Select "Create an App" -* Give the app a name like "Brave Payments Dev" -* Make sure "Enable Sign in with Twitter" is checked -* Set the callback url to `https://localhost:3000/publishers/auth/register_twitter_channel/callback`. If it does not allow you to set `localhost`, use a place holder for now, and later add the correct callback url through apps.twitter.com instead. -* Fill in the remaining information and hit "Create" -* Navigate to your app settings -> permissions and ensure it is readonly and requests the user email -* Regenerate your Consumer API keys -* Update your env to include `TWITCH_CLIENT_ID="your-api-key"` and `TWITTER_CLIENT_SECRET="your-api-secret-key"` -* Save +- Apply for a developer account at [developer.twitter.com](https://developer.twitter.com/) +- Select "Create an App" +- Give the app a name like "Brave Payments Dev" +- Make sure "Enable Sign in with Twitter" is checked +- Set the callback url to `https://localhost:3000/publishers/auth/register_twitter_channel/callback`. If it does not allow you to set `localhost`, use a place holder for now, and later add the correct callback url through apps.twitter.com instead. +- Fill in the remaining information and hit "Create" +- Navigate to your app settings -> permissions and ensure it is readonly and requests the user email +- Regenerate your Consumer API keys +- Update your env to include `TWITCH_CLIENT_ID="your-api-key"` and `TWITTER_CLIENT_SECRET="your-api-secret-key"` +- Save ### reCAPTCHA Setup @@ -147,7 +164,8 @@ To stop using Eyeshade locally, set `API_EYESHADE_BASE_URI=""`. 1. Request access to [Vault-Promo-Services](https://github.com/brave-intl/vault-promo-services) and [ip2tags](https://github.com/brave-intl/vault-promo-services) 2. Follow the [setup instructions](https://github.com/brave-intl/vault-promo-services) -3. Create and run a `vault-promo-services.sh` start script like this +3. Create and run a `vault-promo-services.sh` start script like this + ``` export DATABASE_URL="services" export PGDATABASE="services" @@ -167,11 +185,12 @@ done npm start ``` -* If you run into an issue about a missing `.mmdb` file, run `fetch.sh` in `node_modules/ip2tags` +- If you run into an issue about a missing `.mmdb` file, run `fetch.sh` in `node_modules/ip2tags` + +4. Add the following into your Publishers start script -4. Add the following into your Publishers start script ``` -export API_PROMO_BASE_URI="http://127.0.0.1:8194" +export API_PROMO_BASE_URI="http://127.0.0.1:8194" export API_PROMO_KEY="1234" ``` @@ -203,7 +222,6 @@ rails database_updates:mock_data:populate_promo_stats A picture of the chart generated by the promo server - #### Other vars A few variables are not configured in secrets.yml: currently none @@ -226,7 +244,6 @@ To run simply open the project and run in the terminal yarn lint ``` - ## Testing ### Ruby @@ -242,13 +259,14 @@ On debian you can install it like: ```sh sudo apt-get install chromium ``` + And on mac with: ``` brew cask install chromium ``` -We also use ImageMagick to process user uploaded images. If you don't have it already, you might get an error "You must have ImageMagick or GraphicsMagick installed". You can install on mac with: +We also use ImageMagick to process user uploaded images. If you don't have it already, you might get an error "You must have ImageMagick or GraphicsMagick installed". You can install on mac with: ``` brew install imagemagick @@ -275,6 +293,7 @@ file at the top of the repo. Docker compose will automatically load from this file when launching services. e.g. you might have the following in `.env`: + ``` BAT_MEDIUM_URL=https://medium.com/@attentiontoken BAT_REDDIT_URL=https://www.reddit.com/r/BATProject/ @@ -313,45 +332,50 @@ top of the repo. For example you can expose ports on your system for the databas `docker-compose.override.yml`: ```yaml -version: "2.1" +version: '2.1' services: mongo: ports: - - "27017:27017" + - '27017:27017' redis: ports: - - "6379:6379" + - '6379:6379' postgres: ports: - - "5432:5432" + - '5432:5432' ``` to start with docker build the app and eyeshade images + ```sh docker-compose build ``` and bring up the full stack + ```sh docker-compose up ``` ### Create the databases + ```sh docker-compose run app yarn install; docker-compose run app rake db:setup; docker-compose run eyeshade-worker sh -c "cd eyeshade && ./bin/migrate-up.sh" ``` ### Adding balances to Eyeshade -By default when you create a channel it will not have a balance on Eyeshade, the accounting server. To test wallet code with non nil balances, you must add them first. +By default when you create a channel it will not have a balance on Eyeshade, the accounting server. To test wallet code with non nil balances, you must add them first. To add a contribution to a channel account: + ``` rails "docker:add_contribution_balance_to_account[youtube#channel:UCOo92t8m-tWKgmw276q7mxw, 200]" # Adds 200 BAT to youtube#channel:UCOo92t8m-tWKgmw276q7mxw ``` To add add a referral balance to an owner account: + ``` rails "docker:add_referral_balance_to_account[publishers#uuid:967a9919-34f4-4ce6-af36-e3f592a6eab7, 400]" # Adds 400 BAT to youtube#channel:UCOo92t8m-tWKgmw276q7mxw ``` @@ -361,6 +385,7 @@ The new balance should be reflected on the dashboard. ### Run Tests Tests can be run on the container with + ```sh docker-compose run app rake test ``` @@ -368,6 +393,7 @@ docker-compose run app rake test Other one off commands can be run as above, but replacing `rake test`. Note this spawns a new container. ### Debugging + Debugging with byebug and pry can be done by attaching to the running process. First get the container id with `docker ps` @@ -386,6 +412,7 @@ docker attach 234f116cd942 ``` To connect with a bash shell on a running container use: + ```sh docker exec -i -t 234f116cd942 /bin/bash root@234f116cd942:/var/www# From 1253053aebaff37d0e6f11feb585745c8830ba96 Mon Sep 17 00:00:00 2001 From: Cory McDonald Date: Mon, 3 Jun 2019 13:13:16 -0500 Subject: [PATCH 07/15] Fix Channel Transfers (#1936) * ApproveChannelTransfers Capture exception * Take out extra content * Can't raise any exception in a job * Remove outside of transaction * Fix the expected raise --- app/jobs/delete_publisher_channel_job.rb | 6 +++++- app/jobs/publisher_removal_job.rb | 1 + app/jobs/two_factor_authentication_removal_job.rb | 3 ++- app/services/channels/approve_channel_transfer.rb | 4 +++- app/services/channels/reject_channel_transfer.rb | 2 +- test/jobs/delete_publisher_channel_job_test.rb | 6 ++---- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/jobs/delete_publisher_channel_job.rb b/app/jobs/delete_publisher_channel_job.rb index 233d6a0d8a..8e2112e87f 100644 --- a/app/jobs/delete_publisher_channel_job.rb +++ b/app/jobs/delete_publisher_channel_job.rb @@ -4,7 +4,11 @@ class DeletePublisherChannelJob < ApplicationJob def perform(channel_id:) @channel = Channel.find(channel_id) publisher = @channel.publisher - raise "Can't remove a channel that is contesting another a channel." if @channel.verification_pending? && !publisher.registered_for_2fa_removal? + + if @channel.verification_pending? && !publisher.registered_for_2fa_removal? + Rails.logger.info("Can't remove a channel #{@channel.id} that is contesting another a channel") + return false + end # If channel is being contested, approve the channel which will also delete if @channel.contested_by_channel.present? diff --git a/app/jobs/publisher_removal_job.rb b/app/jobs/publisher_removal_job.rb index 0c7fe96107..9612ed9732 100644 --- a/app/jobs/publisher_removal_job.rb +++ b/app/jobs/publisher_removal_job.rb @@ -16,6 +16,7 @@ def perform(publisher_id:) publisher.update(current_sign_in_ip: nil) publisher.update(last_sign_in_ip: nil) end + publisher.channels.pluck(:id).each do |channel_id| DeletePublisherChannelJob.perform_now(channel_id: channel_id) end diff --git a/app/jobs/two_factor_authentication_removal_job.rb b/app/jobs/two_factor_authentication_removal_job.rb index dd751594b8..91372bf9d3 100644 --- a/app/jobs/two_factor_authentication_removal_job.rb +++ b/app/jobs/two_factor_authentication_removal_job.rb @@ -18,7 +18,8 @@ def perform publisher.u2f_registrations.destroy_all if !publisher.u2f_registrations.empty? if !publisher.channels.empty? publisher.channels.each do |channel| - DeletePublisherChannelJob.perform_now(channel_id: channel.id) + is_deleted = DeletePublisherChannelJob.perform_now(channel_id: channel.id) + raise ActiveRecord::Rollback unless is_deleted end end publisher.uphold_connection&.disconnect_uphold diff --git a/app/services/channels/approve_channel_transfer.rb b/app/services/channels/approve_channel_transfer.rb index 252208d9b1..0d38036407 100644 --- a/app/services/channels/approve_channel_transfer.rb +++ b/app/services/channels/approve_channel_transfer.rb @@ -18,7 +18,9 @@ def perform channel_name = @channel.publication_title # Delete the channel from eyeshade and clean up the promo registration - DeletePublisherChannelJob.perform_now(channel_id: @channel.id) + is_deleted = DeletePublisherChannelJob.perform_now(channel_id: @channel.id) + # If couldn't delete the channel roll back the transaction + raise ActiveRecord::Rollback unless is_deleted contested_by.verified = true contested_by.verification_pending = false diff --git a/app/services/channels/reject_channel_transfer.rb b/app/services/channels/reject_channel_transfer.rb index 12db4ddad4..3e41983553 100644 --- a/app/services/channels/reject_channel_transfer.rb +++ b/app/services/channels/reject_channel_transfer.rb @@ -10,7 +10,7 @@ def initialize(channel:, should_delete: true) end def perform - ActiveRecord::Base.transaction do + ActiveRecord::Base.transaction do # Remove contention from original verified channel @channel.contested_by_channel_id = nil @channel.contest_token = nil diff --git a/test/jobs/delete_publisher_channel_job_test.rb b/test/jobs/delete_publisher_channel_job_test.rb index be3b60947c..f5c4794f57 100644 --- a/test/jobs/delete_publisher_channel_job_test.rb +++ b/test/jobs/delete_publisher_channel_job_test.rb @@ -67,15 +67,13 @@ class DeletePublisherChannelJobTest < ActionDispatch::IntegrationTest assert_nil channel.contesting_channel end - test "destroying a contested_by raises error" do + test "destroying a contested_by returns false" do channel = channels(:fraudulently_verified_site) contested_by_channel = channels(:locked_out_site) # contest channel Channels::ContestChannel.new(channel: channel, contested_by: contested_by_channel).perform - assert_raises do - DeletePublisherChannelJob.perform_now(channel_id: contested_by_channel.id) - end + refute DeletePublisherChannelJob.perform_now(channel_id: contested_by_channel.id) # ensure the unverified channle is gone assert Channel.where(id: contested_by_channel.id).present? From 2d05a1899e1ba08a400b7f5197631ecfb108e623 Mon Sep 17 00:00:00 2001 From: "Dan A. Lipeles" Date: Tue, 4 Jun 2019 13:12:30 -0400 Subject: [PATCH 08/15] Remove duplicate call to PublisherWalletDisconnector (#1946) --- app/jobs/two_factor_authentication_removal_job.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/jobs/two_factor_authentication_removal_job.rb b/app/jobs/two_factor_authentication_removal_job.rb index 91372bf9d3..5f55922fbd 100644 --- a/app/jobs/two_factor_authentication_removal_job.rb +++ b/app/jobs/two_factor_authentication_removal_job.rb @@ -22,8 +22,7 @@ def perform raise ActiveRecord::Rollback unless is_deleted end end - publisher.uphold_connection&.disconnect_uphold - PublisherWalletDisconnector.new(publisher: publisher).perform + publisher.uphold_connection.disconnect_uphold if publisher.uphold_connection.present? publisher.status_updates.create(status: PublisherStatusUpdate::LOCKED) two_factor_authentication_removal.update(removal_completed: true) end From edde5834e5b2936c5d3485a92ad68940d0acd63f Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Tue, 4 Jun 2019 17:21:35 -0700 Subject: [PATCH 09/15] Update youtube_channel_details.rb --- app/models/youtube_channel_details.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/youtube_channel_details.rb b/app/models/youtube_channel_details.rb index 4877a5b15e..060bbb5407 100644 --- a/app/models/youtube_channel_details.rb +++ b/app/models/youtube_channel_details.rb @@ -1,5 +1,4 @@ class YoutubeChannelDetails < BaseChannelDetails - validate :youtube_channel_not_changed_once_initialized validates :youtube_channel_id, presence: true validates :title, presence: true From d872cf0b9c423c4929ff90957542cb43d18db9b1 Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Tue, 4 Jun 2019 17:21:48 -0700 Subject: [PATCH 10/15] Update twitter_channel_details.rb --- app/models/twitter_channel_details.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/twitter_channel_details.rb b/app/models/twitter_channel_details.rb index 3a18460bf3..d8af63417c 100644 --- a/app/models/twitter_channel_details.rb +++ b/app/models/twitter_channel_details.rb @@ -1,5 +1,4 @@ class TwitterChannelDetails < BaseChannelDetails - validate :twitter_channel_not_changed_once_initialized validates :twitter_channel_id, presence: true validates :thumbnail_url, presence: true From 7469bb88eb3c51bc1d4783e7dd11039561440b9e Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Tue, 4 Jun 2019 17:21:59 -0700 Subject: [PATCH 11/15] Update twitch_channel_details.rb --- app/models/twitch_channel_details.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/twitch_channel_details.rb b/app/models/twitch_channel_details.rb index 09c20eccfa..690cdf5c0d 100644 --- a/app/models/twitch_channel_details.rb +++ b/app/models/twitch_channel_details.rb @@ -1,5 +1,4 @@ class TwitchChannelDetails < BaseChannelDetails - validate :twitch_channel_not_changed_once_initialized validates :twitch_channel_id, presence: true validates :thumbnail_url, presence: true From 0c6b431bfcf7b7c32779bdd557f008bad8ef0609 Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Tue, 4 Jun 2019 17:22:16 -0700 Subject: [PATCH 12/15] Update site_channel_details.rb --- app/models/site_channel_details.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/site_channel_details.rb b/app/models/site_channel_details.rb index 1f54355884..c6cd2a6965 100644 --- a/app/models/site_channel_details.rb +++ b/app/models/site_channel_details.rb @@ -1,5 +1,4 @@ class SiteChannelDetails < BaseChannelDetails - # brave_publisher_id is a normalized identifier provided by eyeshade API # It is like base domain (eTLD + left part) but may include additional # formats to support more publishers. From 8851ba4b0463b6a371a3779beee6e0b91dc5a413 Mon Sep 17 00:00:00 2001 From: Cory McDonald Date: Wed, 5 Jun 2019 08:49:16 -0500 Subject: [PATCH 13/15] Fix 500 when user logs in without being on IP Whitelist (#1943) * Fix 500 when user logs in without VPN * Fix lint --- .../stylesheets/admin/errors/whitelist.scss | 152 ++++++++++ app/assets/stylesheets/admin/main.scss | 1 + app/controllers/application_controller.rb | 4 + app/views/admin/errors/_construction.svg.html | 259 ++++++++++++++++++ app/views/admin/errors/whitelist.html.slim | 20 ++ .../admin/publishers_controller_test.rb | 8 +- 6 files changed, 440 insertions(+), 4 deletions(-) create mode 100644 app/assets/stylesheets/admin/errors/whitelist.scss create mode 100644 app/views/admin/errors/_construction.svg.html create mode 100644 app/views/admin/errors/whitelist.html.slim diff --git a/app/assets/stylesheets/admin/errors/whitelist.scss b/app/assets/stylesheets/admin/errors/whitelist.scss new file mode 100644 index 0000000000..a964c0db78 --- /dev/null +++ b/app/assets/stylesheets/admin/errors/whitelist.scss @@ -0,0 +1,152 @@ +h1 { + font-size: 48px; + line-height: 58px; + font-weight: 700; + padding: 0 0 20px; + color: #434352; +} + +h2 { + color: #434352; + font-size: 18px; + font-weight: 500; + line-height: 34px; + padding: 0 0 20px; + margin: 0 0 20px; +} + +.header { + width: 100%; + height: 4.75rem; + background: linear-gradient(-45deg, #2a1fad 0%, #a91b78 100%); + display: flex; + align-items: center; + padding: 0 60px; +} + +.header img { + height: 30px; +} + +.page { + display: flex; + justify-content: center; + padding: 0 60px; +} + +.container { + display: flex; + width: 960px; + margin: 70px 0 0 0; + flex-direction: column; +} + +.box { + display: flex; + flex-direction: column; + flex: 1; + text-align: center; + justify-content: center; + align-items: center; +} + +.box img { + width: 80%; +} +.box h2 { + max-width: 550px; +} + +@media (max-width: 980px) { + .container { + width: 800px; + } + + h1 { + font-size: 40px; + line-height: 50px; + } + + h2 { + font-size: 18px; + } +} + +@media (max-width: 740px) { + h1 { + font-size: 34px; + padding: 0 0 10px; + } + .page { + padding: 0 30px; + } + .header { + padding: 0 30px; + } + .container { + flex-direction: column; + } + .box img { + width: 80%; + } + ul { + margin: 0 0 70px; + } +} + +@-webkit-keyframes bounce { + from, + 20%, + 53%, + 80%, + to { + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + transform: translate3d(0, 0, 0); + } + + 40%, + 43% { + animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + transform: translate3d(0, -30px, 0); + } + + 70% { + animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + transform: translate3d(0, -15px, 0); + } + + 90% { + transform: translate3d(0, -4px, 0); + } +} + +@keyframes bounce { + from, + 20%, + 53%, + 80%, + to { + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + transform: translate3d(0, 0, 0); + } + + 40%, + 43% { + animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + transform: translate3d(0, -30px, 0); + } + + 70% { + animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + transform: translate3d(0, -15px, 0); + } + + 90% { + transform: translate3d(0, -4px, 0); + } +} + +.bounce { + animation-name: bounce; + transform-origin: center bottom; +} diff --git a/app/assets/stylesheets/admin/main.scss b/app/assets/stylesheets/admin/main.scss index bff892bd07..d84e981c89 100644 --- a/app/assets/stylesheets/admin/main.scss +++ b/app/assets/stylesheets/admin/main.scss @@ -15,3 +15,4 @@ // Admin pages @import "pages/**/*"; @import "tooltip"; +@import "errors/whitelist"; diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e87eda0dfb..bfc4a70da3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -15,6 +15,10 @@ class ApplicationController < ActionController::Base before_action :set_paper_trail_whodunnit before_action :no_cache + rescue_from Ability::AdminNotOnIPWhitelistError do |e| + render file: "admin/errors/whitelist.html", layout: false + end + def no_cache return if controller_name == 'static' # We want to cache on the homepage response.headers['Cache-Control'] = 'no-cache, no-store' diff --git a/app/views/admin/errors/_construction.svg.html b/app/views/admin/errors/_construction.svg.html new file mode 100644 index 0000000000..fcdcb16748 --- /dev/null +++ b/app/views/admin/errors/_construction.svg.html @@ -0,0 +1,259 @@ + + +illustration +Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/views/admin/errors/whitelist.html.slim b/app/views/admin/errors/whitelist.html.slim new file mode 100644 index 0000000000..08ed0c85e7 --- /dev/null +++ b/app/views/admin/errors/whitelist.html.slim @@ -0,0 +1,20 @@ +doctype html +html lang="en" + head + meta charset="UTF-8" / + meta content="width=device-width, initial-scale=1.0" name="viewport" / + meta content="ie=edge" http-equiv="X-UA-Compatible" / + = stylesheet_link_tag "admin/main" + title Check your IP Address + body + header.header + .logo + = render("logo_wordmark_white_svg") + content.page + .container + .box + =render("./admin/errors/construction.svg") + .box + h1 Check your IP Address + .box + h2 You might have have forgotten to connect to a VPN or a secure connection. diff --git a/test/controllers/admin/publishers_controller_test.rb b/test/controllers/admin/publishers_controller_test.rb index 3aeb4362cb..f02d87b9ea 100644 --- a/test/controllers/admin/publishers_controller_test.rb +++ b/test/controllers/admin/publishers_controller_test.rb @@ -121,13 +121,13 @@ def stub_verification_public_file(channel, body: nil, status: 200) end end - test "raises error unless admin is on admin whitelist" do + test "renders whitelist unless admin is on admin whitelist" do admin = publishers(:admin) sign_in admin - assert_raises(Ability::AdminNotOnIPWhitelistError) do - get admin_publishers_path, headers: { 'REMOTE_ADDR' => '1.2.3.4' } # not on whitelist - end + get admin_publishers_path, headers: { 'REMOTE_ADDR' => '1.2.3.4' } # not on whitelist + + assert_template "admin/errors/whitelist.html" end test "admins can approve channels waiting for admin approval" do From 9ec12b56f013fc8d4a4707435abc7676ef51bc04 Mon Sep 17 00:00:00 2001 From: Cory McDonald Date: Wed, 5 Jun 2019 14:15:36 -0500 Subject: [PATCH 14/15] Resolve CVE-2015-9284 (#1942) * Add mitigation against CVE-2015-9284 * Change OAuth links to be POST * Fix some tests --- Gemfile | 1 + Gemfile.lock | 4 +++ .../publishers/_choose_channel_type.html.slim | 8 ++--- .../omniauth_callbacks_controller_test.rb | 34 +++++++++---------- .../site_channels_controller_test.rb | 2 +- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Gemfile b/Gemfile index 4aec7a616f..3bed5086b6 100644 --- a/Gemfile +++ b/Gemfile @@ -35,6 +35,7 @@ gem 'cancancan' # Authentication gem "devise", "~> 4.6.1" +gem 'omniauth-rails_csrf_protection', '~> 0.1.1' gem "dnsruby", "~> 1.60.0", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 6adc0bb890..8dc6e650da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -270,6 +270,9 @@ GEM omniauth-oauth2 (1.6.0) oauth2 (~> 1.1) omniauth (~> 1.9) + omniauth-rails_csrf_protection (0.1.1) + actionpack (>= 4.2) + omniauth (>= 1.3.1) omniauth-twitch (1.0.0) omniauth-oauth2 (~> 1.1) omniauth-twitter (1.4.0) @@ -538,6 +541,7 @@ DEPENDENCIES newrelic_rpm (~> 3.16) nokogiri omniauth-google-oauth2 (~> 0.5.2) + omniauth-rails_csrf_protection (~> 0.1.1) omniauth-twitch omniauth-twitter omniauth-vimeo diff --git a/app/views/publishers/_choose_channel_type.html.slim b/app/views/publishers/_choose_channel_type.html.slim index 77b17c4bb4..75b494f077 100644 --- a/app/views/publishers/_choose_channel_type.html.slim +++ b/app/views/publishers/_choose_channel_type.html.slim @@ -11,7 +11,7 @@ h6= t "publishers.choose_new_channel_type.website.heading" = t "publishers.choose_new_channel_type.website.description" -= link_to(publisher_register_youtube_channel_omniauth_authorize_path, class: 'card block-link--card', :"data-piwik-action" => "AddYoutubeChannelClicked", :"data-piwik-name" => "Clicked", :"data-piwik-value" => "AddChannelFlow") do += link_to(publisher_register_youtube_channel_omniauth_authorize_path, class: 'card block-link--card', method: :post, :"data-piwik-action" => "AddYoutubeChannelClicked", :"data-piwik-name" => "Clicked", :"data-piwik-value" => "AddChannelFlow") do .card-body .icon-and-text--wrapper .icon-and-text--icon @@ -20,7 +20,7 @@ h6= t "publishers.choose_new_channel_type.youtube.heading" = t "publishers.choose_new_channel_type.youtube.description" -= link_to(publisher_register_twitch_channel_omniauth_authorize_path, class: 'card block-link--card', :"data-piwik-action" => "AddTwitchChannelClicked", :"data-piwik-name" => "Clicked", :"data-piwik-value" => "AddChannelFlow") do += link_to(publisher_register_twitch_channel_omniauth_authorize_path, class: 'card block-link--card', method: :post, :"data-piwik-action" => "AddTwitchChannelClicked", :"data-piwik-name" => "Clicked", :"data-piwik-value" => "AddChannelFlow") do .card-body .icon-and-text--wrapper .icon-and-text--icon @@ -29,7 +29,7 @@ h6= t "publishers.choose_new_channel_type.twitch.heading" = t "publishers.choose_new_channel_type.twitch.description" -= link_to(publisher_register_twitter_channel_omniauth_authorize_path, class: 'card block-link--card', :"data-piwik-action" => "AddTwitterChannelClicked", :"data-piwik-name" => "Clicked", :"data-piwik-value" => "AddChannelFlow") do += link_to(publisher_register_twitter_channel_omniauth_authorize_path, class: 'card block-link--card', method: :post, :"data-piwik-action" => "AddTwitterChannelClicked", :"data-piwik-name" => "Clicked", :"data-piwik-value" => "AddChannelFlow") do .card-body .icon-and-text--wrapper .icon-and-text--icon @@ -39,7 +39,7 @@ = t "publishers.choose_new_channel_type.twitter.description" - if params[:vimeo] - = link_to(publisher_register_vimeo_channel_omniauth_authorize_path, class: 'card block-link--card', :"data-piwik-action" => "AddVimeoChannelClicked", :"data-piwik-name" => "Clicked", :"data-piwik-value" => "AddChannelFlow") do + = link_to(publisher_register_vimeo_channel_omniauth_authorize_path, class: 'card block-link--card', method: :post, :"data-piwik-action" => "AddVimeoChannelClicked", :"data-piwik-name" => "Clicked", :"data-piwik-value" => "AddChannelFlow") do .card-body .icon-and-text--wrapper .icon-and-text--icon diff --git a/test/controllers/publishers/omniauth_callbacks_controller_test.rb b/test/controllers/publishers/omniauth_callbacks_controller_test.rb index ac516b0e58..c43b0218ce 100644 --- a/test/controllers/publishers/omniauth_callbacks_controller_test.rb +++ b/test/controllers/publishers/omniauth_callbacks_controller_test.rb @@ -89,7 +89,7 @@ def channel_data(options={}) to_return(status: 200, body: { items: [channel_data] }.to_json, headers: {}) assert_difference("Channel.count", 1) do - get(publisher_register_youtube_channel_omniauth_authorize_url) + post(publisher_register_youtube_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path end @@ -120,7 +120,7 @@ def channel_data(options={}) to_return(status: 200, body: { items: [channel_data("id" => "323541525412313421")] }.to_json, headers: {}) assert_difference("Channel.count", 1) do - get(publisher_register_youtube_channel_omniauth_authorize_url) + post(publisher_register_youtube_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! @@ -148,7 +148,7 @@ def channel_data(options={}) to_return(status: 200, body: { items: [channel_data("id" => "323541525412313421")] }.to_json, headers: {}) assert_difference("Channel.count", 1) do - get(publisher_register_youtube_channel_omniauth_authorize_url) + post(publisher_register_youtube_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! @@ -167,7 +167,7 @@ def channel_data(options={}) follow_redirect! - get(publisher_register_youtube_channel_omniauth_authorize_url) + post(publisher_register_youtube_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! @@ -208,7 +208,7 @@ def channel_data(options={}) to_return(status: 200, body: { items: [channel_data("id" => "78032")] }.to_json, headers: {}) assert_difference("Channel.count", 0) do - get(publisher_register_youtube_channel_omniauth_authorize_url) + post(publisher_register_youtube_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! @@ -249,7 +249,7 @@ def auth_hash options={} test "a publisher who only has a google plus email can login using their login channel" do OmniAuth.config.mock_auth[:youtube_login] = auth_hash - get(publisher_youtube_login_omniauth_authorize_url) + post(publisher_youtube_login_omniauth_authorize_url) follow_redirect! assert_redirected_to change_email_publishers_path end @@ -264,7 +264,7 @@ def auth_hash options={} } ) - get(publisher_youtube_login_omniauth_authorize_url) + post(publisher_youtube_login_omniauth_authorize_url) follow_redirect! assert_redirected_to log_in_publishers_path end @@ -272,7 +272,7 @@ def auth_hash options={} test "a publisher who was registered by youtube channel signup can't add additional youtube channels" do OmniAuth.config.mock_auth[:youtube_login] = auth_hash - get(publisher_youtube_login_omniauth_authorize_url) + post(publisher_youtube_login_omniauth_authorize_url) follow_redirect! assert_redirected_to change_email_publishers_path @@ -317,7 +317,7 @@ def auth_hash options={} to_return(status: 200, body: { items: [register_youtube_channel_data] }.to_json, headers: {}) assert_difference("Channel.count", 0) do - get(publisher_register_youtube_channel_omniauth_authorize_url) + post(publisher_register_youtube_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! @@ -362,7 +362,7 @@ def auth_hash(options={}) OmniAuth.config.mock_auth[:register_twitch_channel] = auth_hash assert_difference("Channel.count", 1) do - get(publisher_register_twitch_channel_omniauth_authorize_url) + post(publisher_register_twitch_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path end @@ -386,7 +386,7 @@ def auth_hash(options={}) OmniAuth.config.mock_auth[:register_twitch_channel] = auth_hash("uid" => verified_details[:twitch_channel_id]) assert_difference("Channel.count", 0) do - get(publisher_register_twitch_channel_omniauth_authorize_url) + post(publisher_register_twitch_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! @@ -446,7 +446,7 @@ def auth_hash(options={}) OmniAuth.config.mock_auth[:register_twitter_channel] = auth_hash assert_difference("Channel.count", 1) do - get(publisher_register_twitter_channel_omniauth_authorize_url) + post(publisher_register_twitter_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path end @@ -475,7 +475,7 @@ def auth_hash(options={}) OmniAuth.config.mock_auth[:register_twitter_channel] = auth_hash("uid" => "abc124") assert_difference("Channel.count", 1) do - get(publisher_register_twitter_channel_omniauth_authorize_url) + post(publisher_register_twitter_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! @@ -498,7 +498,7 @@ def auth_hash(options={}) OmniAuth.config.mock_auth[:register_twitter_channel] = auth_hash("uid" => verified_details[:twitter_channel_id]) assert_difference("Channel.count", 0) do - get(publisher_register_twitter_channel_omniauth_authorize_url) + post(publisher_register_twitter_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! @@ -556,7 +556,7 @@ def auth_hash(options={}) OmniAuth.config.mock_auth[:register_vimeo_channel] = auth_hash assert_difference("Channel.count", 1) do - get(publisher_register_vimeo_channel_omniauth_authorize_url) + post(publisher_register_vimeo_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path end @@ -583,7 +583,7 @@ def auth_hash(options={}) ) assert_difference("Channel.count", 1) do - get(publisher_register_vimeo_channel_omniauth_authorize_url) + post(publisher_register_vimeo_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! @@ -609,7 +609,7 @@ def auth_hash(options={}) ) assert_difference("Channel.count", 0) do - get(publisher_register_vimeo_channel_omniauth_authorize_url) + post(publisher_register_vimeo_channel_omniauth_authorize_url) follow_redirect! assert_redirected_to home_publishers_path follow_redirect! diff --git a/test/controllers/site_channels_controller_test.rb b/test/controllers/site_channels_controller_test.rb index 8a0072f8e2..6859c90c1f 100644 --- a/test/controllers/site_channels_controller_test.rb +++ b/test/controllers/site_channels_controller_test.rb @@ -190,7 +190,7 @@ class SiteChannelsControllerTest < ActionDispatch::IntegrationTest } ) - get(publisher_youtube_login_omniauth_authorize_url) + post(publisher_youtube_login_omniauth_authorize_url) follow_redirect! assert_redirected_to change_email_publishers_path From bc28d2adae80c366d2762d863b3f2a1a63ecb45b Mon Sep 17 00:00:00 2001 From: Cory McDonald Date: Wed, 5 Jun 2019 14:15:47 -0500 Subject: [PATCH 15/15] Tackle some Uphold errors (#1937) --- app/jobs/cache_browser_channels_json_job.rb | 2 +- app/jobs/clean_stale_uphold_data_job.rb | 4 ++-- app/jobs/create_uphold_cards_job.rb | 6 +++++- app/jobs/upload_uphold_access_parameters_job.rb | 6 ++++++ app/models/uphold_connection.rb | 2 +- app/services/publisher_wallet_disconnector.rb | 5 +++-- app/services/publisher_wallet_setter.rb | 4 ---- app/services/slack_messenger.rb | 2 ++ app/views/admin/publishers/show.html.slim | 10 +++++----- test/services/publisher_wallet_disconnector_test.rb | 6 ++---- 10 files changed, 27 insertions(+), 20 deletions(-) diff --git a/app/jobs/cache_browser_channels_json_job.rb b/app/jobs/cache_browser_channels_json_job.rb index 3c742102ed..d1801d843c 100644 --- a/app/jobs/cache_browser_channels_json_job.rb +++ b/app/jobs/cache_browser_channels_json_job.rb @@ -19,7 +19,7 @@ def perform if result Rails.logger.info("CacheBrowserChannelsJsonJob updated the cached browser channels json.") else - SlackMessenger.new(message: "🚨 CacheBrowserChannelsJsonJob could not update the channels JSON. @publishers-team 🚨") + SlackMessenger.new(message: "🚨 CacheBrowserChannelsJsonJob could not update the channels JSON. @publishers-team 🚨", channel: SlackMessenger::ALERTS) Rails.logger.info("CacheBrowserChannelsJsonJob could not update the channels JSON.") end end diff --git a/app/jobs/clean_stale_uphold_data_job.rb b/app/jobs/clean_stale_uphold_data_job.rb index 46362ed1af..c206906236 100644 --- a/app/jobs/clean_stale_uphold_data_job.rb +++ b/app/jobs/clean_stale_uphold_data_job.rb @@ -2,7 +2,6 @@ class CleanStaleUpholdDataJob < ApplicationJob queue_as :scheduler def perform - require "sentry-raven" # clear uphold codes sitting for over 5 minutes connections = UpholdConnection.has_stale_uphold_code n = 0 @@ -25,8 +24,9 @@ def perform connection.save! n += 1 Rails.logger.info("Cleaned stalled uphold access parameters for #{connection.publisher.owner_identifier}.") - Raven.capture_message("Cleaned stalled uphold access parameters for #{connection.publisher.owner_identifier}.") + SlackMessenger.new(message: "Cleaned stalled uphold access parameters for #{connection.publisher.owner_identifier}.", channel: SlackMessenger::ALERTS).perform end Rails.logger.info("CleanStaleUpholdDataJob cleared #{n} stalled uphold access parameters.") + SlackMessenger.new(message: "CleanStaleUpholdDataJob cleared #{n} stalled uphold access parameters.", channel: SlackMessenger::ALERTS).perform if n.positive? end end diff --git a/app/jobs/create_uphold_cards_job.rb b/app/jobs/create_uphold_cards_job.rb index b2bd7290ef..f093bad912 100644 --- a/app/jobs/create_uphold_cards_job.rb +++ b/app/jobs/create_uphold_cards_job.rb @@ -5,7 +5,11 @@ class CreateUpholdCardsJob < ApplicationJob def perform(publisher_id:) publisher = Publisher.find(publisher_id) - raise unless publisher.uphold_connection.can_create_uphold_cards? + unless publisher.uphold_connection.can_create_uphold_cards? + Rails.logger.info("Could not create uphold card for publisher #{publisher.id}.") + SlackMessenger.new(message: "Could not create uphold card for publisher #{publisher.id}.", channel: SlackMessenger::ALERTS).perform + return + end default_currency = publisher.default_currency diff --git a/app/jobs/upload_uphold_access_parameters_job.rb b/app/jobs/upload_uphold_access_parameters_job.rb index 9b1d4a6287..7fe9b2d6f3 100644 --- a/app/jobs/upload_uphold_access_parameters_job.rb +++ b/app/jobs/upload_uphold_access_parameters_job.rb @@ -4,6 +4,12 @@ class UploadUpholdAccessParametersJob < ApplicationJob def perform(publisher_id:) publisher = Publisher.find(publisher_id) + if publisher.uphold_connection.uphold_access_parameters.blank? + Rails.logger.info("Publisher #{publisher.id} is missing uphold_access_parameters. UpholdConnection #{publisher.uphold_connection.to_json}") + SlackMessenger.new(message: "🤔 Publisher #{publisher.id} is missing uphold_access_parameters.", channel: SlackMessenger::ALERTS).perform + return + end + PublisherWalletSetter.new(publisher: publisher).perform publisher.uphold_connection.verify_uphold diff --git a/app/models/uphold_connection.rb b/app/models/uphold_connection.rb index 4c2284beb9..2241e54e91 100644 --- a/app/models/uphold_connection.rb +++ b/app/models/uphold_connection.rb @@ -128,6 +128,6 @@ def encryption_key private def send_blocked_message - SlackMessenger.new(message: "Publisher #{id} is blocked by Uphold and has just logged in. ").perform + SlackMessenger.new(message: "Publisher #{id} is blocked by Uphold and has just logged in. ", channel: SlackMessenger::ALERTS).perform end end diff --git a/app/services/publisher_wallet_disconnector.rb b/app/services/publisher_wallet_disconnector.rb index ba9675b4af..caad20b207 100644 --- a/app/services/publisher_wallet_disconnector.rb +++ b/app/services/publisher_wallet_disconnector.rb @@ -9,8 +9,9 @@ def initialize(publisher:) def perform return perform_offline if Rails.application.secrets[:api_eyeshade_offline] - if publisher.uphold_connection.uphold_verified - raise "Publisher #{publisher.id} has re-verified their Uphold connection, so it should not be disconnected." + if publisher.uphold_connection&.uphold_verified + SlackMessenger.new(message: "️🤔 Publisher #{publisher.id} has re-verified their Uphold connection, so it should not be disconnected.", channel: SlackMessenger::ALERTS) + return end # This raises when response is not 2xx. diff --git a/app/services/publisher_wallet_setter.rb b/app/services/publisher_wallet_setter.rb index e1963201f0..3e3306cfdd 100644 --- a/app/services/publisher_wallet_setter.rb +++ b/app/services/publisher_wallet_setter.rb @@ -12,10 +12,6 @@ def perform # (Albert Wang): Unlikely this will happen. Just a safeguard return false if publisher.excluded_from_payout? - if !publisher.uphold_connection.uphold_access_parameters - raise "Publisher #{publisher.id} is missing uphold_access_parameters." - end - uphold_access_parameters = JSON.parse(publisher.uphold_connection.uphold_access_parameters) uphold_access_parameters[:server] = Rails.application.secrets[:uphold_api_uri] diff --git a/app/services/slack_messenger.rb b/app/services/slack_messenger.rb index 9fe6bbd678..cb1d75b244 100644 --- a/app/services/slack_messenger.rb +++ b/app/services/slack_messenger.rb @@ -2,6 +2,8 @@ class SlackMessenger < BaseApiClient attr_reader :channel, :message + ALERTS = "creator-alerts" + def can_perform? !!api_base_uri end diff --git a/app/views/admin/publishers/show.html.slim b/app/views/admin/publishers/show.html.slim index d3ce272420..e7c66c9191 100644 --- a/app/views/admin/publishers/show.html.slim +++ b/app/views/admin/publishers/show.html.slim @@ -33,17 +33,17 @@ hr .db-info-row .db-field = "Organization:" .db-value = link_to partner.organization, admin_organization_path(partner.organization) - - if @publisher.wallet.uphold_account_status.present? + - if @publisher.wallet&.uphold_account_status.present? .db-info-row .db-field = "Uphold status:" .db-value = @publisher.wallet.uphold_account_status .db-info-row - .db-field = "Uphold member:" - .db-value = @publisher.wallet.is_a_member? - -if @publisher.uphold_id.present? + .db-field = "Uphold member (KYC):" + .db-value = @publisher.wallet&.is_a_member? + -if @publisher&.uphold_connection.uphold_id.present? .db-info-row .db-field = "Uphold account id:" - .db-value = link_to @publisher.uphold_id, admin_publishers_path(q: @publisher.uphold_id) + .db-value = link_to @publisher.uphold_connection.uphold_id, admin_publishers_path(q: @publisher.uphold_connection.uphold_id) .db-info-row .db-field = "Uphold widget enabled:" .db-value = @publisher.excluded_from_payout? ? "No" : "Yes" diff --git a/test/services/publisher_wallet_disconnector_test.rb b/test/services/publisher_wallet_disconnector_test.rb index a86d6e1425..c86fd38e29 100644 --- a/test/services/publisher_wallet_disconnector_test.rb +++ b/test/services/publisher_wallet_disconnector_test.rb @@ -47,7 +47,7 @@ class PublisherWalletDisconnectorTest < ActiveJob::TestCase end end - test "raises if uphold has been reverified" do + test "returns nil if uphold has been reverified" do prev_offline = Rails.application.secrets[:api_eyeshade_offline] begin Rails.application.secrets[:api_eyeshade_offline] = false @@ -55,9 +55,7 @@ class PublisherWalletDisconnectorTest < ActiveJob::TestCase publisher = publishers(:verified) publisher.uphold_connection.uphold_verified = true - assert_raises("Publisher #{publisher.id} has re-verified their Uphold connection, so it should not be disconnected.") do - PublisherWalletDisconnector.new(publisher: publisher).perform - end + refute PublisherWalletDisconnector.new(publisher: publisher).perform ensure Rails.application.secrets[:api_eyeshade_offline] = prev_offline end