-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
53 changed files
with
3,235 additions
and
12,186 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
class Api::Nextv1::ChannelsController < Api::Nextv1::BaseController | ||
include ChannelsHelper | ||
|
||
before_action :authenticate_publisher! | ||
|
||
before_action :setup_current_channel | ||
|
||
attr_reader :current_channel | ||
|
||
def destroy | ||
success = DeletePublisherChannelJob.perform_now(current_channel.id) | ||
|
||
if success | ||
head :no_content | ||
else | ||
render(json: {errors: current_channel.errors}, status: 400) | ||
end | ||
end | ||
|
||
private | ||
|
||
def setup_current_channel | ||
@current_channel = current_publisher.channels.find(params[:id]) | ||
rescue ActiveRecord::RecordNotFound | ||
render(json: {}, status: 404) | ||
end | ||
end |
57 changes: 57 additions & 0 deletions
57
app/controllers/api/nextv1/connection/bitflyer_connections_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
require "uri" | ||
require "net/http" | ||
require "json" | ||
require "digest" | ||
require "base64" | ||
|
||
class Api::Nextv1::Connection::BitflyerConnectionsController < Api::Nextv1::Oauth2Controller | ||
def destroy | ||
I18n.locale = :ja | ||
bitflyer_connection = current_publisher.bitflyer_connection | ||
|
||
# Destroy our database records | ||
if bitflyer_connection.destroy | ||
render(json: {}, status: 200) | ||
else | ||
render(json: {errors: I18n.t( | ||
"publishers.gemini_connections.destroy.error", | ||
errors: bitflyer_connection.errors.full_messages.join(", ") | ||
)}, status: 417) | ||
end | ||
end | ||
|
||
private | ||
|
||
# 1.) Set required state for Oauth2 Implementation | ||
# @debug is an optional flag that will return a json response from the callback | ||
# Helpful for explicit debugging and introspection of access token request response values. | ||
def set_controller_state | ||
@klass = BitflyerConnection | ||
@access_token_response = Oauth2::Responses::BitflyerAccessTokenResponse | ||
end | ||
|
||
# 2.) Bitflyer uses code exchange verification: https://datatracker.ietf.org/doc/html/rfc7636#section-4.1 | ||
def code_challenge | ||
Digest::SHA256.base64digest(code_verifier).chomp("=").tr("+", "-").tr("/", "_") | ||
end | ||
|
||
# One way encoded(Varies through time + unique to provider + random/varies through sesion) | ||
def code_verifier | ||
Digest::SHA256.base64digest(current_publisher.current_sign_in_at.to_s + current_publisher.id + current_publisher.session_salt.to_s) | ||
end | ||
|
||
# 3.) Generate auth_url using code_challange verification | ||
def authorization_url | ||
@_authorization_url ||= client.authorization_code_url( | ||
state: @state, | ||
scope: @klass.oauth2_config.scope, | ||
code_challenge: code_challenge, | ||
code_challenge_method: "S256" | ||
) | ||
end | ||
|
||
# 4.) Make request using code_verifier | ||
def access_token_request | ||
client.access_token(params.require(:code), code_verifier: code_verifier) | ||
end | ||
end |
25 changes: 25 additions & 0 deletions
25
app/controllers/api/nextv1/connection/gemini_connections_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
class Api::Nextv1::Connection::GeminiConnectionsController < Api::Nextv1::Oauth2Controller | ||
def destroy | ||
gemini_connection = current_publisher.gemini_connection | ||
|
||
# Destroy our database records | ||
if gemini_connection.destroy | ||
render(json: {}, status: 200) | ||
else | ||
render(json: {errors: I18n.t( | ||
"publishers.gemini_connections.destroy.error", | ||
errors: gemini_connection.errors.full_messages.join(", ") | ||
)}, status: 417) | ||
end | ||
end | ||
|
||
private | ||
|
||
# 1.) Set required state for Oauth2 Implementation | ||
# @debug is an optional flag that will return a json response from the callback | ||
# Helpful for explicit debugging and introspection of access token request response values. | ||
def set_controller_state | ||
@klass = GeminiConnection | ||
# @debug = true | ||
end | ||
end |
62 changes: 62 additions & 0 deletions
62
app/controllers/api/nextv1/connection/uphold_connections_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# NOTE: To test this locally you have to access the app from 127.0.0.1: Uphold does not allow localhost has a valid domain | ||
# and when you redirect back to 127.0.0.1 all your relevant cookies are lost. | ||
# | ||
# When you login through the email url, you need to copy the url and replace localhost with 127.0.0.1 | ||
# and create your session with that domain. | ||
# | ||
# Took me a while to figure that out. | ||
|
||
class Api::Nextv1::Connection::UpholdConnectionsController < Api::Nextv1::Oauth2Controller | ||
include PublishersHelper | ||
|
||
def show | ||
publisher = current_publisher | ||
render(json: { | ||
uphold_status: publisher.uphold_connection&.uphold_status.to_s, | ||
uphold_is_member: publisher.uphold_connection&.is_member? || false, | ||
uphold_status_summary: uphold_status_summary(publisher), | ||
uphold_status_description: uphold_status_description(publisher), | ||
default_currency: publisher.uphold_connection&.default_currency, | ||
uphold_username: publisher.uphold_connection&.uphold_details&.username | ||
}, status: 200) | ||
end | ||
|
||
# TODO: Do we update connections??? | ||
# def update | ||
# uphold_connection = current_publisher.uphold_connection | ||
# return if uphold_connection.blank? | ||
|
||
# send_emails = DateTime.now | ||
|
||
# case params[:send_emails] | ||
# when "forever" | ||
# send_emails = UpholdConnection::FOREVER_DATE | ||
# when "next_year" | ||
# send_emails = 1.year.from_now | ||
# end | ||
|
||
# uphold_connection.update(send_emails: send_emails) | ||
# end | ||
|
||
# publishers/disconnect_uphold | ||
def destroy | ||
# You can't remove your connection if you've been banned/suspended. | ||
# This is how we prevent you from reusing the connection. | ||
if !current_publisher.authorized_to_act? # | ||
head :unauthorized and return | ||
end | ||
|
||
current_publisher&.uphold_connection&.destroy | ||
head :ok | ||
end | ||
|
||
private | ||
|
||
# 1.) Set required state for Oauth2 Implementation | ||
# @debug is an optional flag that will return a json response from the callback | ||
# Helpful for explicit debugging and introspection of access token request response values. | ||
def set_controller_state | ||
@klass = UpholdConnection | ||
@klass.strict_create = true # toggle various restrictions on creating wallets. Useful for debugging. | ||
end | ||
end |
119 changes: 119 additions & 0 deletions
119
app/controllers/api/nextv1/crypto_address_for_channels_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
require "eth" | ||
require "rbnacl" | ||
require "base58" | ||
|
||
class Api::Nextv1::CryptoAddressForChannelsController < Api::Nextv1::BaseController | ||
include PublishersHelper | ||
include Eth | ||
|
||
def index | ||
current_channel = current_publisher.channels.find(params[:channel_id]) | ||
@crypto_addresses_for_channel = CryptoAddressForChannel.where(channel: current_channel) | ||
render(json: @crypto_addresses_for_channel) | ||
end | ||
|
||
def generate_nonce | ||
nonce = SecureRandom.uuid | ||
Rails.cache.write(nonce, current_publisher.id) | ||
render json: {nonce: nonce} | ||
end | ||
|
||
def create | ||
signature = params[:transaction_signature] | ||
account_address = params[:account_address] | ||
chain = params[:chain] | ||
message = params[:message] | ||
current_channel = current_publisher.channels.find(params[:channel_id]) | ||
@errors = [] | ||
|
||
# check that the message is a valid nonce and delete after use | ||
valid_message = if Rails.cache.read(message) == current_publisher.id | ||
!!Rails.cache.delete(message) | ||
else | ||
@errors << "message is invalid" | ||
false | ||
end | ||
|
||
# check to make sure the user owns the address | ||
verified = valid_message && | ||
case chain | ||
when "SOL" | ||
verify_solana_address(signature, account_address, message) | ||
when "ETH" | ||
verify_ethereum_address(signature, account_address, message) | ||
else | ||
@errors << "address could not be verified" | ||
false | ||
end | ||
|
||
# Create new crypto address, and remove any other addresses on the same chain for the channel | ||
success = verified && replace_crypto_address_for_channel(account_address, chain, current_channel) | ||
|
||
if success | ||
render(json: {crypto_address_for_channel: success}, status: 201) | ||
else | ||
render(json: {errors: @errors}, status: 400) | ||
end | ||
end | ||
|
||
def change_address | ||
account_address = params[:address] | ||
chain = params[:chain] | ||
current_channel = current_publisher.channels.find(params[:channel_id]) | ||
|
||
success = replace_crypto_address_for_channel(account_address, chain, current_channel) | ||
|
||
if success | ||
render(json: {crypto_address_for_channel: success}, status: 200) | ||
else | ||
render(json: {errors: "address could not be updated"}, status: 400) | ||
end | ||
end | ||
|
||
def destroy | ||
chain = params[:chain] | ||
current_channel = current_publisher.channels.find(params[:channel_id]) | ||
|
||
begin | ||
CryptoAddressForChannel.where(chain: chain, channel: current_channel).first&.destroy! | ||
render(json: {crypto_address_for_channel: true}, status: 200) | ||
rescue => e | ||
LogException.perform(e, publisher: current_publisher) | ||
render(json: {errors: "address could not be deleted"}, status: 400) | ||
end | ||
end | ||
|
||
def verify_solana_address(signature, address, message) | ||
verify_key = RbNaCl::VerifyKey.new(Base58.base58_to_binary(address, :bitcoin)) | ||
verify_key.verify(Base58.base58_to_binary(signature, :bitcoin), message) | ||
rescue => e | ||
LogException.perform(e, publisher: current_publisher) | ||
false | ||
end | ||
|
||
def verify_ethereum_address(signature, address, message) | ||
signature_pubkey = Eth::Signature.personal_recover message, signature | ||
signature_address = Eth::Util.public_key_to_address signature_pubkey | ||
# Eth addresses are case insensitive | ||
signature_address.address.downcase == address.downcase | ||
rescue => e | ||
LogException.perform(e, publisher: current_publisher) | ||
false | ||
end | ||
|
||
def replace_crypto_address_for_channel(account_address, chain, channel) | ||
ActiveRecord::Base.transaction do | ||
crypto_address = CryptoAddress.where(publisher: current_publisher, address: account_address, chain: chain, verified: true).first_or_create! | ||
existing_address = CryptoAddressForChannel.where(chain: chain, channel: channel) | ||
|
||
if existing_address.length > 0 | ||
existing_address.first.destroy! | ||
end | ||
|
||
CryptoAddressForChannel.create!(chain: chain, crypto_address: crypto_address, channel: channel) | ||
end | ||
rescue => e | ||
LogException.perform(e, publisher: current_publisher) | ||
false | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
class Api::Nextv1::CryptoAddressesController < Api::Nextv1::BaseController | ||
include PublishersHelper | ||
|
||
def index | ||
@crypto_addresses = CryptoAddress.where(publisher_id: current_publisher.id) | ||
render(json: @crypto_addresses) | ||
end | ||
|
||
def destroy | ||
begin | ||
@crypto_address = current_publisher.crypto_addresses.find(params[:id]) | ||
rescue ActiveRecord::RecordNotFound | ||
return render json: {}, status: 404 | ||
end | ||
|
||
begin | ||
@crypto_address.destroy! | ||
render(json: {crypto_address: true}, status: 200) | ||
rescue => e | ||
LogException.perform(e, publisher: current_publisher) | ||
render(json: {errors: "address could not be deleted"}, status: 400) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
class Api::Nextv1::HomeController < Api::Nextv1::BaseController | ||
include PublishersHelper | ||
|
||
def dashboard | ||
publisher = current_publisher.as_json(only: [:id], methods: [:brave_payable?]) | ||
channels = current_publisher.channels.visible.as_json(only: [:details_type, :id, :verified, :verification_status, :verification_details], | ||
methods: [:failed_verification_details, :failed_verification_call_to_action], | ||
include: { | ||
details: {only: [], methods: [:publication_title]} | ||
}) | ||
wallet = PublisherWalletGetter.new( | ||
publisher: current_publisher, | ||
include_transactions: true | ||
).perform | ||
regions = Rewards::Parameters.new.fetch_allowed_regions | ||
|
||
wallet_data = { | ||
wallet: wallet, | ||
uphold_connection: uphold_wallet, | ||
gemini_connection: gemini_wallet, | ||
bitflyer_connection: bitflyer_wallet, | ||
allowed_regions: regions, | ||
next_deposit_date: next_deposit_date | ||
} | ||
|
||
response_data = { | ||
publisher: publisher, | ||
channels: channels, | ||
wallet_data: wallet_data | ||
} | ||
|
||
render(json: response_data.to_json, status: 200) | ||
end | ||
|
||
# TODO: figure out if we need the 'latest' endpoint | ||
# Public: Requests the Publisher's latest transactions from Eyeshade | ||
# | ||
# Returns the latest settled payment in JSON | ||
def latest | ||
wallet = PublisherWalletGetter.new( | ||
publisher: current_publisher, | ||
include_transactions: true | ||
).perform | ||
|
||
render json: {lastSettlement: wallet.last_settlement_balance} | ||
end | ||
|
||
private | ||
|
||
# Internal: Renders properties associated with an Uphold Wallet Connection | ||
# | ||
# Returns a hash | ||
def uphold_wallet | ||
current_publisher.uphold_connection.as_json( | ||
only: [:default_currency, :uphold_id, :is_member, :oauth_refresh_failed, :payout_failed], | ||
methods: [:can_create_uphold_cards?, :username, :uphold_status, :verify_url] | ||
) | ||
end | ||
|
||
# Internal: Renders properties associated with the Gemini Wallet Connection | ||
# | ||
# Returns a hash | ||
def gemini_wallet | ||
current_publisher.gemini_connection.as_json( | ||
only: [:default_currency, :display_name, :recipient_id, :oauth_refresh_failed, :recipient_id_status, :payout_failed], | ||
methods: [:payable?, :verify_url, :valid_country?] | ||
) | ||
end | ||
|
||
# Internal: Renders properties associated with the Bitflyer Wallet Connection | ||
# | ||
# Returns a hash | ||
def bitflyer_wallet | ||
current_publisher.bitflyer_connection.as_json( | ||
only: [:default_currency, :display_name, :recipient_id, :oauth_refresh_failed, :payout_failed], | ||
methods: [:payable?, :verify_url] | ||
) | ||
end | ||
end |
Oops, something went wrong.