회원인증

This commit is contained in:
Envy_PC 2025-01-24 16:26:50 +09:00
parent 476765fbc9
commit 0d8a8dc04e
14 changed files with 545 additions and 0 deletions

0
src/__init__.py Normal file
View File

67
src/sessionManager.py Normal file
View File

@ -0,0 +1,67 @@
from supabase import create_client, Client
import jwt
import time
class SupabaseSessionManager:
_instance = None # 싱글톤 인스턴스
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(SupabaseSessionManager, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self, supabase_url: str, supabase_key: str):
if not hasattr(self, "initialized"):
self.supabase: Client = create_client(supabase_url, supabase_key)
self.access_token = None
self.refresh_token = None
self.expiration_time = None
self.initialized = True # 초기화 완료 여부 플래그
def login(self, email: str, password: str):
try:
response = self.supabase.auth.sign_in_with_password({"email": email, "password": password})
self._update_tokens(response)
print("로그인 성공!")
return True
except Exception as e:
print(f"로그인 실패: {e}")
return False
def _update_tokens(self, response):
# Access Token 및 Refresh Token 갱신
self.access_token = response.session.access_token
self.refresh_token = response.session.refresh_token
# Access Token 만료 시간 계산
decoded_token = jwt.decode(self.access_token, options={"verify_signature": False})
self.expiration_time = decoded_token.get("exp", 0)
def ensure_valid_access_token(self):
"""
Access Token이 만료되었는지 확인하고, 필요시 Refresh Token을 사용해 갱신.
"""
now = int(time.time())
if self.expiration_time is None or now >= self.expiration_time - 60: # 만료 1분 전
print("Access Token이 만료되었거나 만료 예정입니다. 갱신 중...")
try:
response = self.supabase.auth.refresh_session(self.refresh_token)
self._update_tokens(response)
print("Access Token 갱신 완료!")
except Exception as e:
print(f"Access Token 갱신 실패: {e}")
raise Exception("새로 로그인이 필요합니다.") # 필요 시 예외 발생
def get_supabase_client(self):
"""
항상 유효한 Access Token을 보장하는 Supabase 클라이언트를 반환.
"""
self.ensure_valid_access_token()
return self.supabase
def get_access_token(self):
"""
Access Token 반환. 필요한 경우 갱신 반환.
"""
self.ensure_valid_access_token()
return self.access_token

View File

@ -0,0 +1,79 @@
import asyncio
import jwt
import time
from supabase import create_client, Client
from concurrent.futures import ThreadPoolExecutor
class SupabaseSessionManagerAsync:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(SupabaseSessionManagerAsync, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self, supabase_url: str, supabase_key: str):
if not hasattr(self, "initialized"):
self.supabase: Client = create_client(supabase_url, supabase_key)
self.access_token = None
self.refresh_token = None
self.expiration_time = None
self.executor = ThreadPoolExecutor() # 동기 작업을 비동기로 처리하기 위한 스레드 풀
self.initialized = True
async def login(self, email: str, password: str):
"""
비동기 로그인 메서드.
"""
loop = asyncio.get_event_loop()
try:
response = await loop.run_in_executor(self.executor, self.supabase.auth.sign_in_with_password, {"email": email, "password": password})
self._update_tokens(response)
print("로그인 성공!")
return True
except Exception as e:
print(f"로그인 실패: {e}")
return False
def _update_tokens(self, response):
"""
Access Token Refresh Token 갱신.
"""
self.access_token = response.session.access_token
self.refresh_token = response.session.refresh_token
# Access Token 만료 시간 설정
decoded_token = jwt.decode(self.access_token, options={"verify_signature": False})
self.expiration_time = decoded_token.get("exp", 0)
async def ensure_valid_access_token(self):
"""
비동기 Access Token 유효성 확인 갱신.
"""
now = int(time.time())
if self.expiration_time is None or now >= self.expiration_time - 60: # 만료 1분 전
print("Access Token 갱신 중...")
try:
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(self.executor, self.supabase.auth.refresh_session, self.refresh_token)
self._update_tokens(response)
print("Access Token 갱신 완료!")
except Exception as e:
print(f"Access Token 갱신 실패: {e}")
raise Exception("새로 로그인이 필요합니다.") # 필요 시 예외 발생
async def fetch_data(self, table_name: str):
"""
비동기 API 호출.
"""
await self.ensure_valid_access_token() # Access Token 유효성 확인
loop = asyncio.get_event_loop()
try:
# Supabase API 호출 비동기로 실행
response = await loop.run_in_executor(self.executor, self.supabase.table(table_name).select("*").execute)
print(f"데이터 조회 성공: {response.data}")
return response.data
except Exception as e:
print(f"데이터 조회 실패: {e}")
return None

Binary file not shown.

27
src/test/sp_test1/db.py Normal file
View File

@ -0,0 +1,27 @@
import sqlite3
from supabase import create_client
# Supabase 설정
SUPABASE_URL = 'http://oci1ckh08045.duckdns.org:8000'
SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE'
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
# SQLite 연결
conn = sqlite3.connect('ForbiddenKeyword.db')
cursor = conn.cursor()
# 로컬 데이터베이스에서 데이터 가져오기
cursor.execute("SELECT keyword, grade, status FROM keywords")
rows = cursor.fetchall()
# Supabase에 데이터 삽입
for row in rows:
keyword, grade, status = row
supabase.table('common_banned_words').insert({
'banned_word': keyword,
'grade': grade,
'status': status
}).execute()
# 연결 종료
conn.close()

44
src/test/sp_test1/db2.py Normal file
View File

@ -0,0 +1,44 @@
import sqlite3
from supabase import create_client
# Supabase 설정
SUPABASE_URL = 'http://oci1ckh08045.duckdns.org:8000'
SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE'
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
# SQLite 연결
conn = sqlite3.connect('ForbiddenKeyword.db')
cursor = conn.cursor()
# 로컬 데이터베이스에서 데이터 가져오기
cursor.execute("""
SELECT
k.id AS keyword_id,
r.application_status,
r.registration_date,
r.applicant_name,
r.classification_code,
r.category_description,
r.drawing,
r.bigDrawing
FROM kipris_results r
INNER JOIN keywords k ON r.keyword_id = k.id
""")
rows = cursor.fetchall()
# Supabase에 데이터 삽입
for row in rows:
keyword_id, application_status, registration_date, application_name, classification_code, category_description, drawing, bigDrawing = row
supabase.table('common_banned_words_for_kipris').insert({
'banned_word_id': keyword_id,
'application_status': application_status,
'registration_date': registration_date,
'applicant_name': application_name,
'classification_code': classification_code,
'category_description': category_description,
'drawing': drawing,
'bigDrawing': bigDrawing
}).execute()
# 연결 종료
conn.close()

View File

@ -0,0 +1,72 @@
from supabase import create_client, Client
import bcrypt
import os
# from dotenv import load_dotenv
# # 환경 변수 로드
# load_dotenv()
# SUPABASE_URL = os.getenv("SUPABASE_URL")
# SUPABASE_KEY = os.getenv("SUPABASE_KEY")
SUPABASE_URL = 'http://oci1ckh08045.duckdns.org:8000'
# anon
# SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE'
# sevice_role
SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q'
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
# 비밀번호 해싱
def hash_password(password: str) -> str:
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
return hashed.decode('utf-8')
# 비밀번호 검증
def verify_password(password: str, hashed_password: str) -> bool:
return bcrypt.checkpw(password.encode('utf-8'), hashed_password.encode('utf-8'))
# 회원가입: Supabase 인증 시스템 사용
def register_user(email: str, password: str, nickname: str) -> bool:
try:
# Supabase auth.users에 사용자 등록
response = supabase.auth.sign_up({"email": email, "password": password})
user_id = response.user.id
# public.users에 사용자 데이터 추가
supabase.table("users").insert({
"id": user_id, # auth.users의 ID 참조
"email": email,
"nickname": nickname,
"membership_level": "basic" # 기본 등급 설정
}).execute()
print("회원가입 성공!")
return True
except Exception as e:
print(f"회원가입 오류: {e}")
return False
# 로그인: Supabase 인증 시스템 사용
def login_user(email: str, password: str) -> dict:
try:
# Supabase auth.users에서 사용자 인증
response = supabase.auth.sign_in_with_password({"email": email, "password": password})
user_id = response.user.id
# public.users에서 추가 정보 조회
response_users = supabase.table("users").select("*").eq("id", user_id).execute()
print(f"user_data: {response_users}") # 데이터 조회 결과 출력
# public.users에서 추가 정보 가져오기
user_data = response_users.data[0] if response_users.data else None
return {
"id": user_id,
"email": response.user.email,
"nickname": user_data["nickname"] if user_data else "Unknown",
}
except Exception as e:
print(f"로그인 오류: {e}")
return None

13
src/test/sp_test1/main.py Normal file
View File

@ -0,0 +1,13 @@
import sys
from PySide6.QtWidgets import QApplication
from ui.login_dialog import LoginDialog
if __name__ == "__main__":
app = QApplication(sys.argv)
login_dialog = LoginDialog()
if login_dialog.exec(): # 로그인 성공 시 실행
print("메인 프로그램 실행!")
# 메인 프로그램 로직 추가
else:
print("프로그램 종료")

View File

@ -0,0 +1,30 @@
from supabase import create_client
# Supabase 설정
SUPABASE_URL = 'http://oci1ckh08045.duckdns.org:8000'
SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE'
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
# 쿼리 실행
def fetch_banned_words():
try:
# status가 '등록'인 banned_word 조회
response = supabase.table('common_banned_words') \
.select('*, common_banned_words_for_kipris (applicant_name)') \
.eq('status', '등록') \
.execute()
# print(f"response : {response}")
# 조회된 banned_word를 기반으로 common_banned_words_kipris 테이블 조회
for item in response.data:
banned_word = item['banned_word']
applicant_names = [x['applicant_name'] for x in item['common_banned_words_for_kipris']]
print(f"banned_word: {banned_word}")
print(f"applicant_names: {applicant_names}")
except Exception as e:
print(f"Error fetching data: {e}")
# 함수 호출
fetch_banned_words()

View File

@ -0,0 +1,78 @@
from supabase import create_client
import requests
# Supabase 설정
SUPABASE_URL = 'http://oci1ckh08045.duckdns.org:8000'
SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE'
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
# 사용자 인증 함수
def sign_in(email, password):
try:
# Supabase 로그인 요청
response = supabase.auth.sign_in_with_password(
{"email": email, "password": password}
)
# session과 user 정보 추출
session = response.session # 세션 정보
user = response.user # 사용자 정보
if session:
print("로그인 성공!")
print(f"Access Token: {session.access_token}")
print(f"Refresh Token: {session.refresh_token}")
print(f"User ID: {user.id}")
return session # 세션 정보 반환
else:
print("로그인 실패: 세션 정보를 찾을 수 없습니다.")
return None
except Exception as e:
print(f"로그인 실패: {e}")
return None
# 사용자 인증 함수
def sign_up(email, password):
try:
response = supabase.auth.sign_up(
{"email": email, "password": password}
)
print(f"response: {response}")
return response
except Exception as e:
print(f"로그인 실패: {e}")
return None
def fetch_banned_words():
try:
response = supabase.table('common_banned_words') \
.select('*, common_banned_words_for_kipris (applicant_name)') \
.eq('status', '등록') \
.execute()
return response.data
except Exception as e:
print(f"데이터 조회 실패: {e}")
return None
# 사용자 정보 입력 및 인증
email = input("Email: ")
password = input("Password: ")
# result = sign_up(email, password)
session = sign_in(email, password)
if session:
data = fetch_banned_words() # 데이터 조회
if data:
for item in data:
banned_word = item['banned_word']
applicant_names = [x['applicant_name'] for x in item['common_banned_words_for_kipris']]
print(f"banned_word: {banned_word}")
print(f"applicant_names: {applicant_names}")
else:
print("데이터가 없습니다.")
else:
print("인증 실패")

View File

@ -0,0 +1,42 @@
from supabase import create_client
SUPABASE_URL = 'http://oci1ckh08045.duckdns.org:8000'
# anon
SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE'
# sevice_role
# SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q'
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
def fetch_common_banned_words(user_id):
try:
# 사용자 등급 확인
user_response = supabase.table("users").select("membership_level").eq("id", user_id).execute()
print(f"user_response: {user_response}")
membership_level = user_response.data[0]["membership_level"]
print(f"membership_level: {membership_level}")
# 등급별 데이터 가져오기
if membership_level == "basic":
response = supabase.table("common_banned_words").select("*").execute()
elif membership_level == "premium":
response = supabase.table("common_banned_words").select("*").execute()
else: # admin 또는 기타 등급
response = supabase.table("common_banned_words").select("*").execute()
return response.data
except Exception as e:
print(f"데이터 조회 실패: {e}")
return None
if __name__ == "__main__":
# 예제: 사용자 ID를 사용해 데이터 조회
user_id = "6c0ae3b3-1dc7-4fde-85f2-1aac4ceddef8"
data = fetch_common_banned_words(user_id)
if data:
for row in data:
print(row)
else:
print("데이터가 없습니다.")

View File

@ -0,0 +1,50 @@
import asyncio
from PySide6.QtWidgets import QDialog, QVBoxLayout, QLineEdit, QPushButton, QLabel
from db_cli import login_user, register_user
from ui.signup_dialog import SignupDialog
class LoginDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("로그인")
self.setGeometry(300, 300, 300, 200)
layout = QVBoxLayout()
self.email_input = QLineEdit(self)
self.email_input.setPlaceholderText("이메일")
layout.addWidget(self.email_input)
self.password_input = QLineEdit(self)
self.password_input.setPlaceholderText("비밀번호")
self.password_input.setEchoMode(QLineEdit.Password)
layout.addWidget(self.password_input)
self.login_button = QPushButton("로그인", self)
self.login_button.clicked.connect(self.handle_login)
layout.addWidget(self.login_button)
self.signup_button = QPushButton("회원가입", self)
self.signup_button.clicked.connect(self.handle_signup)
layout.addWidget(self.signup_button)
self.status_label = QLabel("", self)
layout.addWidget(self.status_label)
self.setLayout(layout)
def handle_login(self):
email = self.email_input.text()
password = self.password_input.text()
user = login_user(email, password)
if user:
self.status_label.setText("로그인 성공!")
print(f"환영합니다, {user['nickname']}!")
self.accept() # 다이얼로그 닫기
else:
self.status_label.setText("로그인 실패. 이메일 또는 비밀번호를 확인하세요.")
def handle_signup(self):
signup_dialog = SignupDialog()
if signup_dialog.exec():
self.status_label.setText("회원가입 성공!")

View File

@ -0,0 +1,43 @@
from PySide6.QtWidgets import QDialog, QVBoxLayout, QLineEdit, QPushButton, QLabel
from db_cli import register_user
class SignupDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("회원가입")
self.setGeometry(300, 300, 300, 200)
layout = QVBoxLayout()
self.email_input = QLineEdit(self)
self.email_input.setPlaceholderText("이메일")
layout.addWidget(self.email_input)
self.password_input = QLineEdit(self)
self.password_input.setPlaceholderText("비밀번호")
self.password_input.setEchoMode(QLineEdit.Password)
layout.addWidget(self.password_input)
self.nickname_input = QLineEdit(self)
self.nickname_input.setPlaceholderText("닉네임")
layout.addWidget(self.nickname_input)
self.register_button = QPushButton("회원가입", self)
self.register_button.clicked.connect(self.handle_register)
layout.addWidget(self.register_button)
self.status_label = QLabel("", self)
layout.addWidget(self.status_label)
self.setLayout(layout)
def handle_register(self):
email = self.email_input.text()
password = self.password_input.text()
nickname = self.nickname_input.text()
success = register_user(email, password, nickname)
if success:
self.status_label.setText("회원가입 성공!")
self.accept() # 다이얼로그 닫기
else:
self.status_label.setText("회원가입 실패. 다시 시도해주세요.")

View File