diff --git a/backend/app/core/config.py b/backend/app/core/config.py index aeac0ff8..5d9ca18d 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -1,5 +1,6 @@ from pydantic_settings import BaseSettings from dotenv import load_dotenv +from pydantic import field_validator load_dotenv() @@ -14,10 +15,9 @@ class Settings(BaseSettings): github_token: str = "" discord_bot_token: str = "" - # TODO: Add DB configuration - # Database - # Supabase - # Weaviate + # DB configuration + supabase_url: str + supabase_key: str # LangSmith Tracing langsmith_tracing: bool = False @@ -32,6 +32,13 @@ class Settings(BaseSettings): agent_timeout: int = 30 max_retries: int = 3 + @field_validator("supabase_url", "supabase_key", mode="before") + @classmethod + def _not_empty(cls, v, field): + if not v: + raise ValueError(f"{field.name} must be set") + return v + class Config: env_file = ".env" extra = "ignore" # to prevent errors from extra env variables diff --git a/backend/app/db/supabase/auth.py b/backend/app/db/supabase/auth.py index ac738246..b5d212c6 100644 --- a/backend/app/db/supabase/auth.py +++ b/backend/app/db/supabase/auth.py @@ -1,25 +1,27 @@ +import asyncio from app.db.supabase.supabase_client import supabase_client -import os -def login_with_oauth(provider: str): + + +async def login_with_oauth(provider: str): try: - result = supabase_client.auth.sign_in_with_oauth({ - "provider": provider, - "options": { - "redirect_to": os.getenv("SUPABASE_REDIRECT_URL", "http://localhost:3000/home") - } - }) + result = await asyncio.to_thread( + supabase_client.auth.sign_in_with_oauth, + {"provider": provider} + ) return {"url": result.url} except Exception as e: - raise Exception(f"OAuth login failed for {provider}: {str(e)}") + raise Exception(f"OAuth login failed for {provider}") from e +async def login_with_github(): + return await login_with_oauth("github") -def login_with_github(): - return login_with_oauth("github") +async def login_with_discord(): + return await login_with_oauth("discord") -def login_with_discord(): - return login_with_oauth("discord") +async def login_with_slack(): + return await login_with_oauth("slack") -def logout(access_token: str): +async def logout(access_token: str): try: supabase_client.auth.set_session(access_token, refresh_token="") supabase_client.auth.sign_out() diff --git a/backend/app/db/supabase/supabase_client.py b/backend/app/db/supabase/supabase_client.py index 4eb72d1d..a0ab208a 100644 --- a/backend/app/db/supabase/supabase_client.py +++ b/backend/app/db/supabase/supabase_client.py @@ -1,14 +1,8 @@ -import os -from dotenv import load_dotenv +from app.core.config import settings from supabase import create_client -load_dotenv() - -SUPABASE_URL = os.getenv("SUPABASE_URL") -SUPABASE_KEY = os.getenv("SUPABASE_KEY") - -if SUPABASE_URL is None or SUPABASE_KEY is None: - raise ValueError("SUPABASE_URL and SUPABASE_KEY must be set in environment variables.") +SUPABASE_URL = settings.supabase_url +SUPABASE_KEY = settings.supabase_key supabase_client = create_client(SUPABASE_URL, SUPABASE_KEY) diff --git a/backend/bots/discord/discord_cogs.py b/backend/bots/discord/discord_cogs.py index 2cf51e8e..2159f86a 100644 --- a/backend/bots/discord/discord_cogs.py +++ b/backend/bots/discord/discord_cogs.py @@ -1,7 +1,12 @@ -from discord.ext import commands import discord +from discord.ext import commands +import logging from app.core.orchestration.queue_manager import AsyncQueueManager, QueuePriority +from app.db.supabase.auth import login_with_github from bots.discord.discord_bot import DiscordBot +from bots.discord.discord_views import OAuthView + +logger = logging.getLogger(__name__) class DevRelCommands(commands.Cog): def __init__(self, bot: DiscordBot, queue_manager: AsyncQueueManager): @@ -33,8 +38,36 @@ async def help_devrel(self, ctx: commands.Context): name="Commands", value=( "• `!reset` – Reset your DevRel thread and memory\n" - "• `!help_devrel` – Show this help message" + "• `!help_devrel` – Show this help message\n" + "• `!verify_github` – Link your GitHub account\n" ), inline=False ) await ctx.send(embed=embed) + + @commands.command(name="verify_github") + async def verify_github(self, ctx: commands.Context): + """Get GitHub verification link.""" + try: + logger.info(f"User {ctx.author.name}({ctx.author.id}) has requested for GitHub verification") + + oauth_result = await login_with_github() + logger.info(f"OAuth result: {oauth_result}") + oauth_url = oauth_result["url"] + + embed = discord.Embed( + title="🔗 Verify GitHub Account", + description="Click the button below to link your GitHub account \nAfter authorization, you'll be redirected to your GitHub profile.", + ) + embed.add_field( + name="ℹ️ Note:", + value="The link expires in 5 minutes for security purposes.", + inline=False + ) + + view = OAuthView(oauth_url, "GitHub") + await ctx.send(embed=embed, view=view) + + except Exception as e: + logger.error(f"Error in verify_github: {str(e)}") + await ctx.send("Error generating GitHub verification link. Please try again.") diff --git a/backend/bots/discord/discord_views.py b/backend/bots/discord/discord_views.py new file mode 100644 index 00000000..db62d5ce --- /dev/null +++ b/backend/bots/discord/discord_views.py @@ -0,0 +1,16 @@ +import discord + +class OAuthView(discord.ui.View): + """View with OAuth button.""" + + def __init__(self, oauth_url: str, provider_name: str): + super().__init__(timeout=300) + self.oauth_url = oauth_url + self.provider_name = provider_name + + button = discord.ui.Button( + label=f"Connect {provider_name}", + style=discord.ButtonStyle.link, + url=oauth_url + ) + self.add_item(button)