mirror of
https://github.com/algora-io/tv.git
synced 2024-11-16 00:58:59 +02:00
feat(wip): add YouTube OAuth (#107)
This commit is contained in:
parent
0a8f716872
commit
32e79ad204
@ -72,6 +72,21 @@ config :phoenix, :json_library, Jason
|
||||
|
||||
config :nx, default_backend: EXLA.Backend
|
||||
|
||||
# ueberauth config
|
||||
config :ueberauth, Ueberauth,
|
||||
providers: [
|
||||
google: {Ueberauth.Strategy.Google, [
|
||||
prompt: "consent",
|
||||
access_type: "offline",
|
||||
default_scope: "email https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtube.upload https://www.googleapis.com/auth/yt-analytics.readonly"
|
||||
]}
|
||||
]
|
||||
|
||||
config :ueberauth, Ueberauth.Strategy.Google.OAuth,
|
||||
client_id: System.get_env("GOOGLE_CLIENT_ID"),
|
||||
client_secret: System.get_env("GOOGLE_CLIENT_SECRET"),
|
||||
redirect_uri: System.get_env("GOOGLE_REDIRECT_URI")
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{config_env()}.exs"
|
||||
|
@ -2,7 +2,7 @@ defmodule Algora.Accounts do
|
||||
import Ecto.Query
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Algora.{Repo, Restream}
|
||||
alias Algora.{Repo, Restream, Google}
|
||||
alias Algora.Accounts.{User, Identity, Destination, Entity}
|
||||
|
||||
def list_users(opts) do
|
||||
@ -166,6 +166,35 @@ defmodule Algora.Accounts do
|
||||
Repo.one(query) != nil
|
||||
end
|
||||
|
||||
def update_google_token(user, %{token: access_token, refresh_token: refresh_token}) do
|
||||
identity = Repo.get_by!(Identity, user_id: user.id, provider: "google")
|
||||
|
||||
Identity.changeset(identity, %{
|
||||
provider_token: access_token,
|
||||
provider_refresh_token: refresh_token
|
||||
})
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def refresh_google_tokens(%User{} = user) do
|
||||
identity =
|
||||
Repo.one!(from(i in Identity, where: i.user_id == ^user.id and i.provider == "google"))
|
||||
|
||||
case Google.refresh_access_token(identity.provider_refresh_token) do
|
||||
{:ok, tokens} ->
|
||||
update_google_token(user, tokens)
|
||||
{:ok, tokens}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def has_google_token?(%User{} = user) do
|
||||
query = from(i in Identity, where: i.user_id == ^user.id and i.provider == "google")
|
||||
Repo.one(query) != nil
|
||||
end
|
||||
|
||||
def get_restream_token(%User{} = user) do
|
||||
query = from(i in Identity, where: i.user_id == ^user.id and i.provider == "restream")
|
||||
|
||||
|
@ -24,6 +24,17 @@ defmodule Algora.Accounts.Identity do
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@doc """
|
||||
sets up a changeset for oauth.
|
||||
for now, its just Google. Perhaps, more providers in the future?
|
||||
"""
|
||||
def changeset(identity, attrs) do
|
||||
identity
|
||||
|> cast(attrs, [:provider, :provider_id, :provider_token, :provider_email, :provider_login, :provider_refresh_token])
|
||||
|> validate_required([:provider, :provider_token, :provider_id, :provider_email])
|
||||
|> validate_length(:provider, min: 1)
|
||||
end
|
||||
|
||||
@doc """
|
||||
A user changeset for github registration.
|
||||
"""
|
||||
|
@ -2,6 +2,7 @@ defmodule Algora.Accounts.User do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Algora.{Repo}
|
||||
alias Algora.Accounts.{User, Identity, Entity}
|
||||
|
||||
schema "users" do
|
||||
@ -49,6 +50,38 @@ defmodule Algora.Accounts.User do
|
||||
end
|
||||
end
|
||||
|
||||
def create_or_update_youtube_identity(user, auth) do
|
||||
attrs = %{
|
||||
provider: to_string(auth.provider),
|
||||
provider_id: auth.uid,
|
||||
provider_email: auth.info.email,
|
||||
provider_meta: auth.info,
|
||||
provider_login: auth.info.email,
|
||||
provider_token: auth.credentials.token,
|
||||
provider_refresh_token: auth.credentials.refresh_token,
|
||||
expires_at: auth.credentials.expires_at
|
||||
}
|
||||
|
||||
case Repo.get_by(Identity, user_id: user.id, provider: "google") do
|
||||
nil -> %Identity{user_id: user.id}
|
||||
identity -> identity
|
||||
end
|
||||
|> Identity.changeset(attrs)
|
||||
|> Repo.insert_or_update()
|
||||
|> case do
|
||||
{:ok, identity} -> {:ok, identity}
|
||||
{:error, changeset} ->
|
||||
{:error, changeset}
|
||||
end
|
||||
end
|
||||
|
||||
def delete_youtube_identity(user) do
|
||||
case Repo.get_by(Identity, user_id: user.id, provider: "google") do
|
||||
nil -> {:error, :not_found}
|
||||
identity -> Repo.delete(identity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
A user changeset for github registration.
|
||||
"""
|
||||
|
38
lib/algora/google.ex
Normal file
38
lib/algora/google.ex
Normal file
@ -0,0 +1,38 @@
|
||||
defmodule Algora.Google do
|
||||
@moduledoc """
|
||||
This module contains Google-related functions.
|
||||
For now, it only contains the function to referesh the user token for the YouTube integration
|
||||
Perhaps, as time goes on, it'll contain more.
|
||||
"""
|
||||
|
||||
def refresh_access_token(refresh_token) do
|
||||
body =
|
||||
URI.encode_query(%{
|
||||
client_id: client_id(),
|
||||
client_secret: client_secret(),
|
||||
refresh_token: refresh_token,
|
||||
grant_type: "refresh_token"
|
||||
})
|
||||
|
||||
headers = [
|
||||
{"Content-Type", "application/x-www-form-urlencoded"}
|
||||
]
|
||||
|
||||
res = HTTPoison.post("https://oauth2.googleapis.com/token", body, headers)
|
||||
|
||||
with {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- res,
|
||||
%{"access_token" => token} = decoded_body <- Jason.decode!(body) do
|
||||
new_refresh_token = Map.get(decoded_body, "refresh_token", refresh_token)
|
||||
{:ok, %{token: token, refresh_token: new_refresh_token}}
|
||||
else
|
||||
{:error, %HTTPoison.Error{reason: reason}} -> {:error, reason}
|
||||
%{} = res -> {:error, {:bad_response, res}}
|
||||
end
|
||||
end
|
||||
|
||||
defp client_id,
|
||||
do: Application.fetch_env!(:ueberauth, Ueberauth.Strategy.Google.OAuth)[:client_id]
|
||||
|
||||
defp client_secret,
|
||||
do: Application.fetch_env!(:ueberauth, Ueberauth.Strategy.Google.OAuth)[:client_secret]
|
||||
end
|
40
lib/algora_web/controllers/youtube_controller.ex
Normal file
40
lib/algora_web/controllers/youtube_controller.ex
Normal file
@ -0,0 +1,40 @@
|
||||
defmodule AlgoraWeb.YoutubeAuthController do
|
||||
use AlgoraWeb, :controller
|
||||
plug Ueberauth
|
||||
plug :ensure_authenticated
|
||||
|
||||
alias Algora.Accounts.User
|
||||
|
||||
def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do
|
||||
user = conn.assigns.current_user
|
||||
|
||||
case User.create_or_update_youtube_identity(user, auth) do
|
||||
{:ok, _identity} ->
|
||||
conn
|
||||
|> put_flash(:info, "Successfully connected your YouTube account.")
|
||||
|> redirect(to: "/channel/settings")
|
||||
|
||||
{:error, _changeset} ->
|
||||
conn
|
||||
|> put_flash(:error, "Error connecting your YouTube account.")
|
||||
|> redirect(to: "/channel/settings")
|
||||
end
|
||||
end
|
||||
|
||||
def callback(%{assigns: %{ueberauth_failure: _failure}} = conn, _params) do
|
||||
conn
|
||||
|> put_flash(:error, "Failed to connect YouTube account.")
|
||||
|> redirect(to: "/channel/settings")
|
||||
end
|
||||
|
||||
defp ensure_authenticated(conn, _opts) do
|
||||
if conn.assigns[:current_user] do
|
||||
conn
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, "You must be logged in to connect your YouTube account.")
|
||||
|> redirect(to: "/auth/login")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
end
|
@ -28,89 +28,128 @@ defmodule AlgoraWeb.SettingsLive do
|
||||
</div>
|
||||
<div class="space-y-6 bg-white/5 rounded-lg p-6 ring-1 ring-white/15">
|
||||
<.header>
|
||||
Stream Connection
|
||||
Stream Connection
|
||||
<:subtitle>
|
||||
Connection details for live streaming with RTMP
|
||||
Connection details for live streaming with RTMP
|
||||
</:subtitle>
|
||||
</.header>
|
||||
<div class="w-full">
|
||||
<div class="flex justify-between items-center">
|
||||
<label class="block text-sm font-semibold leading-6 text-gray-100 mb-2">Stream URL</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="relative w-full">
|
||||
<.input
|
||||
class="w-full p-2.5 test-sm mr-16 py-1 px-2 leading-tight block ext-sm"
|
||||
name="stream_url"
|
||||
value={@stream_url}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
id="copy_stream_url"
|
||||
class="flex-shrink-0 z-10 inline-flex items-center py-3 px-4 ml-2 text-sm font-medium text-center rounded bg-gray-700 hover:bg-gray-600"
|
||||
phx-hook="CopyToClipboard"
|
||||
data-value={@stream_url}
|
||||
data-notice="Copied Stream Url">
|
||||
<span id="default-icon">
|
||||
<svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 20">
|
||||
<path d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z"/>
|
||||
</svg>
|
||||
</span>
|
||||
<span id="success-icon" class="hidden inline-flex items-center">
|
||||
<svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 12">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5.917 5.724 10.5 15 1.5"/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-400">
|
||||
<%= "Paste into OBS Studio > File > Settings > Stream > Server" %>
|
||||
</p>
|
||||
<div class="w-full">
|
||||
<div class="flex justify-between items-center">
|
||||
<label class="block text-sm font-semibold leading-6 text-gray-100 mb-2">Stream URL</label>
|
||||
</div>
|
||||
|
||||
<div class="w-full">
|
||||
<div class="flex justify-between items-center">
|
||||
<label class="block text-sm font-semibold leading-6 text-gray-100 mb-2">Stream Key</label>
|
||||
<div class="flex items-center">
|
||||
<div class="relative w-full">
|
||||
<.input
|
||||
class="w-full p-2.5 test-sm mr-16 py-1 px-2 leading-tight block ext-sm"
|
||||
name="stream_url"
|
||||
value={@stream_url}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<button
|
||||
phx-click="regenerate_stream_key"
|
||||
class="flex-shrink-0 z-10 inline-flex items-center py-2 px-4 mr-2 text-sm font-medium text-center rounded bg-gray-700 hover:bg-gray-600">
|
||||
Generate
|
||||
</button>
|
||||
<div class="relative w-full">
|
||||
<.input
|
||||
id="stream_key"
|
||||
name="stream_key"
|
||||
class="w-full p-2.5 test-sm mr-16 py-1 px-2 leading-tight block ext-sm"
|
||||
value={@stream_key}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
id="copy_stream_key"
|
||||
class="flex-shrink-0 z-10 inline-flex items-center py-3 px-4 ml-2 text-sm font-medium text-center rounded bg-gray-700 hover:bg-gray-600"
|
||||
phx-hook="CopyToClipboard"
|
||||
data-value={@stream_key}
|
||||
data-notice="Copied Stream Key">
|
||||
<span id="default-icon">
|
||||
<svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 20">
|
||||
<path d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z"/>
|
||||
</svg>
|
||||
</span>
|
||||
<span id="success-icon" class="hidden inline-flex items-center">
|
||||
<svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 12">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5.917 5.724 10.5 15 1.5"/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-400">
|
||||
<%= "Paste into OBS Studio > File > Settings > Stream > Stream Key" %>
|
||||
</p>
|
||||
<button
|
||||
id="copy_stream_url"
|
||||
class="flex-shrink-0 z-10 inline-flex items-center py-3 px-4 ml-2 text-sm font-medium text-center rounded bg-gray-700 hover:bg-gray-600"
|
||||
phx-hook="CopyToClipboard"
|
||||
data-value={@stream_url}
|
||||
data-notice="Copied Stream Url"
|
||||
>
|
||||
<span id="default-icon">
|
||||
<svg
|
||||
class="w-4 h-4"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 18 20"
|
||||
>
|
||||
<path d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span id="success-icon" class="hidden inline-flex items-center">
|
||||
<svg
|
||||
class="w-4 h-4"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 16 12"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 5.917 5.724 10.5 15 1.5"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-400">
|
||||
<%= "Paste into OBS Studio > File > Settings > Stream > Server" %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="w-full">
|
||||
<div class="flex justify-between items-center">
|
||||
<label class="block text-sm font-semibold leading-6 text-gray-100 mb-2">Stream Key</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<button
|
||||
phx-click="regenerate_stream_key"
|
||||
class="flex-shrink-0 z-10 inline-flex items-center py-2 px-4 mr-2 text-sm font-medium text-center rounded bg-gray-700 hover:bg-gray-600"
|
||||
>
|
||||
Generate
|
||||
</button>
|
||||
<div class="relative w-full">
|
||||
<.input
|
||||
id="stream_key"
|
||||
name="stream_key"
|
||||
class="w-full p-2.5 test-sm mr-16 py-1 px-2 leading-tight block ext-sm"
|
||||
value={@stream_key}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
id="copy_stream_key"
|
||||
class="flex-shrink-0 z-10 inline-flex items-center py-3 px-4 ml-2 text-sm font-medium text-center rounded bg-gray-700 hover:bg-gray-600"
|
||||
phx-hook="CopyToClipboard"
|
||||
data-value={@stream_key}
|
||||
data-notice="Copied Stream Key"
|
||||
>
|
||||
<span id="default-icon">
|
||||
<svg
|
||||
class="w-4 h-4"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 18 20"
|
||||
>
|
||||
<path d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span id="success-icon" class="hidden inline-flex items-center">
|
||||
<svg
|
||||
class="w-4 h-4"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 16 12"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 5.917 5.724 10.5 15 1.5"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-400">
|
||||
<%= "Paste into OBS Studio > File > Settings > Stream > Stream Key" %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-6 bg-white/5 rounded-lg p-6 ring-1 ring-white/15">
|
||||
<.header>
|
||||
Integrations
|
||||
@ -118,7 +157,7 @@ defmodule AlgoraWeb.SettingsLive do
|
||||
Manage your connected accounts and services
|
||||
</:subtitle>
|
||||
</.header>
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<.button :if={!@connected_with_restream}>
|
||||
<.link href={"/oauth/login/restream?#{URI.encode_query(return_to: "/channel/settings")}"}>
|
||||
Connect with Restream
|
||||
@ -146,6 +185,30 @@ defmodule AlgoraWeb.SettingsLive do
|
||||
<span class="ml-1">Connected with Restream</span>
|
||||
</.link>
|
||||
</.button>
|
||||
<.button :if={!@connected_with_google}>
|
||||
<.link href="/auth/google">
|
||||
Connect with YouTube
|
||||
</.link>
|
||||
</.button>
|
||||
<.button :if={@connected_with_google} class="bg-green-600 hover:bg-green-500 text-white">
|
||||
<.link href="/auth/google" class="flex items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-5 w-5 -ml-0.5"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M5 12l5 5l10 -10" />
|
||||
</svg>
|
||||
<span class="ml-1">Connected with YouTube</span>
|
||||
</.link>
|
||||
</.button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-6 bg-white/5 rounded-lg p-6 ring-1 ring-white/15">
|
||||
@ -227,10 +290,13 @@ defmodule AlgoraWeb.SettingsLive do
|
||||
destinations = Accounts.list_destinations(current_user.id)
|
||||
destination_changeset = Accounts.change_destination(%Destination{})
|
||||
connected_with_restream = Accounts.has_restream_token?(current_user)
|
||||
rtmp_host = case URI.parse(AlgoraWeb.Endpoint.url()).host do
|
||||
"localhost" -> "127.0.0.1"
|
||||
host -> host
|
||||
end
|
||||
connected_with_google = Accounts.has_google_token?(current_user)
|
||||
|
||||
rtmp_host =
|
||||
case URI.parse(AlgoraWeb.Endpoint.url()).host do
|
||||
"localhost" -> "127.0.0.1"
|
||||
host -> host
|
||||
end
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
@ -240,11 +306,12 @@ defmodule AlgoraWeb.SettingsLive do
|
||||
|> assign(destination_form: to_form(destination_changeset))
|
||||
|> assign(show_add_destination_modal: false)
|
||||
|> assign(stream_key: current_user.stream_key)
|
||||
|> assign(connected_with_restream: connected_with_restream),
|
||||
|> assign(connected_with_restream: connected_with_restream)
|
||||
|> assign(connected_with_google: connected_with_google),
|
||||
temporary_assigns: [
|
||||
stream_url: "rtmp://#{rtmp_host}:#{Algora.config([:rtmp_port])}/#{Algora.config([:rtmp_path])}"
|
||||
]
|
||||
}
|
||||
stream_url:
|
||||
"rtmp://#{rtmp_host}:#{Algora.config([:rtmp_port])}/#{Algora.config([:rtmp_path])}"
|
||||
]}
|
||||
end
|
||||
|
||||
def handle_event("validate", %{"user" => params}, socket) do
|
||||
@ -302,6 +369,7 @@ defmodule AlgoraWeb.SettingsLive do
|
||||
socket
|
||||
|> assign(stream_key: user.stream_key)
|
||||
|> put_flash(:info, "Stream key regenerated!")}
|
||||
|
||||
{:error, _changeset} ->
|
||||
{:noreply,
|
||||
socket
|
||||
@ -309,7 +377,7 @@ defmodule AlgoraWeb.SettingsLive do
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("copied_to_clipboard", %{ "notice" => notice }, socket) do
|
||||
def handle_event("copied_to_clipboard", %{"notice" => notice}, socket) do
|
||||
{:noreply, socket |> put_flash(:info, notice)}
|
||||
end
|
||||
|
||||
|
@ -53,6 +53,17 @@ defmodule AlgoraWeb.Router do
|
||||
end
|
||||
end
|
||||
|
||||
scope "/auth", AlgoraWeb do
|
||||
pipe_through :browser
|
||||
|
||||
live_session :auth_login, on_mount: [{AlgoraWeb.UserAuth, :current_user}, AlgoraWeb.Nav] do
|
||||
live "/login", SignInLive, :index
|
||||
end
|
||||
|
||||
get "/:provider", YoutubeAuthController, :request
|
||||
get "/:provider/callback", YoutubeAuthController, :callback
|
||||
end
|
||||
|
||||
scope "/", AlgoraWeb do
|
||||
pipe_through [:browser, :embed]
|
||||
|
||||
@ -136,7 +147,6 @@ defmodule AlgoraWeb.Router do
|
||||
end
|
||||
|
||||
live_session :default, on_mount: [{AlgoraWeb.UserAuth, :current_user}, AlgoraWeb.Nav] do
|
||||
live "/auth/login", SignInLive, :index
|
||||
live "/cossgpt", COSSGPTLive, :index
|
||||
live "/og/cossgpt", COSSGPTOGLive, :index
|
||||
|
||||
|
6
mix.exs
6
mix.exs
@ -86,7 +86,11 @@ defmodule Algora.MixProject do
|
||||
{:ex_aws_s3, "~> 2.3"},
|
||||
{:ex_doc, "~> 0.29.0"},
|
||||
{:hackney, ">= 1.20.1"},
|
||||
{:sweet_xml, ">= 0.0.0", optional: true}
|
||||
{:sweet_xml, ">= 0.0.0", optional: true},
|
||||
# ueberauth
|
||||
{:ueberauth, "~> 0.10"},
|
||||
{:ueberauth_google, "~> 0.10"},
|
||||
{:oauth2, "~> 2.0", override: true},
|
||||
]
|
||||
end
|
||||
|
||||
|
4
mix.lock
4
mix.lock
@ -91,6 +91,7 @@
|
||||
"nx": {:hex, :nx, "0.7.1", "5f6376e3d18408116e8a84b8f4ac851fb07dfe61764a5410ebf0b5dcb69c1b7e", [:mix], [{:complex, "~> 0.5", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e3ddd6a3f2a9bac79c67b3933368c25bb5ec814a883fc68aba8fd8a236751777"},
|
||||
"nx_image": {:hex, :nx_image, "0.1.2", "0c6e3453c1dc30fc80c723a54861204304cebc8a89ed3b806b972c73ee5d119d", [:mix], [{:nx, "~> 0.4", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "9161863c42405ddccb6dbbbeae078ad23e30201509cc804b3b3a7c9e98764b81"},
|
||||
"nx_signal": {:hex, :nx_signal, "0.2.0", "e1ca0318877b17c81ce8906329f5125f1e2361e4c4235a5baac8a95ee88ea98e", [:mix], [{:nx, "~> 0.6", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "7247e5e18a177a59c4cb5355952900c62fdeadeb2bad02a9a34237b68744e2bb"},
|
||||
"oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"},
|
||||
"oban": {:hex, :oban, "2.17.3", "ddfd5710aadcd550d2e174c8d73ce5f1865601418cf54a91775f20443fb832b7", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "452eada8bfe0d0fefd0740ab5fa8cf3ef6c375df0b4a3c3805d179022a04738a"},
|
||||
"open_api_spex": {:hex, :open_api_spex, "3.19.1", "65ccb5d06e3d664d1eec7c5ea2af2289bd2f37897094a74d7219fb03fc2b5994", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "392895827ce2984a3459c91a484e70708132d8c2c6c5363972b4b91d6bbac3dd"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
|
||||
@ -128,12 +129,15 @@
|
||||
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
|
||||
"tesla": {:hex, :tesla, "1.12.1", "fe2bf4250868ee72e5d8b8dfa408d13a00747c41b7237b6aa3b9a24057346681", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2391efc6243d37ead43afd0327b520314c7b38232091d4a440c1212626fdd6e7"},
|
||||
"thumbnex": {:hex, :thumbnex, "0.5.0", "9f3c20c8c70d17e108710830e1495548b45c7433f30dc318f1075d76eb6f7f00", [:mix], [{:ffmpex, "~> 0.10.0", [hex: :ffmpex, repo: "hexpm", optional: false]}, {:mogrify, "~> 0.9.0", [hex: :mogrify, repo: "hexpm", optional: false]}], "hexpm", "a187948110e2de8dc2e9a73d5a3489398ba6a44d285293c174b6285717c5e5fc"},
|
||||
"timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"},
|
||||
"tokenizers": {:hex, :tokenizers, "0.4.0", "140283ca74a971391ddbd83cd8cbdb9bd03736f37a1b6989b82d245a95e1eb97", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "ef1a9824f5a893cd3b831c0e5b3d72caa250d2ec462035cc6afef6933b13a82e"},
|
||||
"turbojpeg": {:hex, :turbojpeg, "0.4.0", "02616e44a70788e40bfc1fdbbdd8bc4e4615cd7d5ced5614b2aefb60a8acbda7", [:mix], [{:bundlex, "~> 1.4.0", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "53759d41f6e7d63805dc014db11b5c8e9274c5e67caea46d8b7f314dcdf51431"},
|
||||
"typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"},
|
||||
"tzdata": {:hex, :tzdata, "1.1.2", "45e5f1fcf8729525ec27c65e163be5b3d247ab1702581a94674e008413eef50b", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cec7b286e608371602318c414f344941d5eb0375e14cfdab605cca2fe66cba8b"},
|
||||
"ueberauth": {:hex, :ueberauth, "0.10.8", "ba78fbcbb27d811a6cd06ad851793aaf7d27c3b30c9e95349c2c362b344cd8f0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f2d3172e52821375bccb8460e5fa5cb91cfd60b19b636b6e57e9759b6f8c10c1"},
|
||||
"ueberauth_google": {:hex, :ueberauth_google, "0.12.1", "90cf49743588193334f7a00da252f92d90bfd178d766c0e4291361681fafec7d", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.10.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "7f7deacd679b2b66e3bffb68ecc77aa1b5396a0cbac2941815f253128e458c38"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
||||
"unifex": {:hex, :unifex, "1.1.2", "ed3366515b6612a5a08d24a38658dea18a6c6001e79cf41e3a2edd07004d3c6d", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}, {:shmex, "~> 0.5.0", [hex: :shmex, repo: "hexpm", optional: false]}], "hexpm", "c25b9d4d1a1c76716ecdf68d0873553fdab4105f418ef76f646a5cb47e0396ab"},
|
||||
"unpickler": {:hex, :unpickler, "0.1.0", "c2262c0819e6985b761e7107546cef96a485f401816be5304a65fdd200d5bd6a", [:mix], [], "hexpm", "e2b3f61e62406187ac52afead8a63bfb4e49394028993f3c4c42712743cab79e"},
|
||||
|
Loading…
Reference in New Issue
Block a user