Integrazione YouTube Live: OAuth squadra, canale piattaforma Light e UI.

Collegamento da Dettagli squadra e admin per il refresh token Match Live TV; API e app mobile allineate ai piani Light/Full.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-02 19:36:56 +02:00
parent 1a84d6ae42
commit 994c1e3c09
28 changed files with 557 additions and 28 deletions

View File

@@ -0,0 +1,13 @@
module Admin
class YoutubeController < Admin::BaseController
def platform
if ENV["YOUTUBE_CLIENT_ID"].blank?
redirect_to admin_root_path, alert: "Configura YOUTUBE_CLIENT_ID e YOUTUBE_CLIENT_SECRET in .env"
return
end
state = Youtube::OauthState.for_platform(admin_id: current_admin_account.id)
redirect_to Youtube::OauthUrl.build(state: state), allow_other_host: true
end
end
end

View File

@@ -70,6 +70,7 @@ module Api
def team_json(team, detail: false)
ent = team.entitlements
yt = Youtube::TeamStatus.new(team)
data = {
id: team.id,
name: team.name,
@@ -79,7 +80,10 @@ module Api
secondary_color: team.effective_secondary_color,
club_id: team.club_id,
club_name: team.club.name,
youtube_connected: team.youtube_credential.present?,
youtube_connected: yt.connected?,
youtube_selectable: yt.selectable?,
youtube_channel_title: yt.channel_title,
youtube_uses_platform_channel: yt.uses_platform_channel?,
plan_slug: ent.plan.slug,
plan_name: ent.plan.name,
premium_active: ent.premium_active?,

View File

@@ -1,25 +1,70 @@
module Api
module V1
class YoutubeController < BaseController
include ActionController::Cookies
skip_before_action :authenticate_request!, only: :callback
def authorize
team = current_user.manageable_teams.find(params[:team_id])
team.entitlements.assert_can_connect_youtube!
url = Youtube::OauthUrl.build(team_id: team.id, redirect_uri: ENV.fetch("YOUTUBE_REDIRECT_URI"))
state = Youtube::OauthState.for_team(team_id: team.id, user_id: current_user.id)
url = Youtube::OauthUrl.build(state: state, redirect_uri: ENV.fetch("YOUTUBE_REDIRECT_URI"))
render json: { authorization_url: url }
end
def callback
team = current_user.manageable_teams.find(params[:state])
return redirect_to_oauth_error("Codice OAuth mancante") if params[:code].blank?
ctx = Youtube::OauthState.verify!(params[:state])
tokens = Youtube::OauthExchange.call(params[:code])
if ctx[:kind] == :platform
unless platform_oauth_authorized?(ctx[:admin_id])
return redirect_to "#{admin_base_url}/login", alert: "Sessione admin richiesta", allow_other_host: true
end
return render_platform_token(tokens)
end
user = User.find(ctx[:user_id])
team = user.manageable_teams.find(ctx[:team_id])
cred = team.youtube_credential || team.build_youtube_credential
cred.update!(
access_token: tokens[:access_token],
refresh_token: tokens[:refresh_token],
refresh_token: tokens[:refresh_token] || cred.refresh_token,
expires_at: Time.current + tokens[:expires_in].seconds,
channel_id: tokens[:channel_id],
channel_title: tokens[:channel_title]
)
redirect_to "/admin/teams/#{team.id}?youtube=connected"
redirect_to "#{public_base_url}/teams/#{team.id}/dettagli?youtube=connected", allow_other_host: true
rescue ArgumentError, Youtube::BroadcastService::Error => e
redirect_to_oauth_error(e.message)
end
private
def public_base_url
MatchLiveTv.app_public_url.chomp("/")
end
def redirect_to_oauth_error(message)
redirect_to "#{public_base_url}/prezzi?youtube_error=#{ERB::Util.url_encode(message)}",
allow_other_host: true
end
def render_platform_token(tokens)
@refresh_token = tokens[:refresh_token]
@channel_title = tokens[:channel_title]
render "admin/youtube/platform_token", layout: "admin"
end
def platform_oauth_authorized?(admin_id)
session[:admin_account_id].present? && session[:admin_account_id].to_s == admin_id.to_s
end
def admin_base_url
"#{public_base_url}/admin"
end
end
end

View File

@@ -7,6 +7,7 @@ module Public
before_action :set_team, only: %i[
details dashboard roster edit update invite create_invitation
assign_self_staff clear_self_staff remove_member destroy_invitation
youtube_connect youtube_disconnect
]
def new
@@ -114,6 +115,27 @@ module Public
redirect_to public_team_details_path(@team), notice: "Invito annullato"
end
def youtube_connect
require_club_owner_for_team!(@team)
@team.entitlements.assert_can_connect_youtube!
if ENV["YOUTUBE_CLIENT_ID"].blank?
redirect_to public_team_details_path(@team), alert: "YouTube OAuth non configurato sul server"
return
end
state = Youtube::OauthState.for_team(team_id: @team.id, user_id: current_user.id)
redirect_to Youtube::OauthUrl.build(state: state), allow_other_host: true
rescue Teams::EntitlementError => e
redirect_to public_team_details_path(@team), alert: e.message
end
def youtube_disconnect
require_club_owner_for_team!(@team)
@team.youtube_credential&.destroy!
redirect_to public_team_details_path(@team), notice: "Canale YouTube scollegato"
end
private
def load_team_details!