From d80a0ad464a8e36c98e4fb962cf944e25fadf357 Mon Sep 17 00:00:00 2001 From: CPABONG Date: Sun, 24 Aug 2025 18:31:27 +0900 Subject: [PATCH] =?UTF-8?q?=EC=84=B1=EB=8A=A5=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EC=9D=B8=EC=A6=9D=EC=98=A4=EB=A5=98=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=ED=85=94=EB=A0=88=EA=B7=B8=EB=9E=A8=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=ED=8F=AC=ED=95=A8=20=EB=93=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- A_core/__pycache__/settings.cpython-38.pyc | Bin 4794 -> 5001 bytes A_core/__pycache__/sms_utils.cpython-38.pyc | Bin 5138 -> 6752 bytes .../__pycache__/telegram_utils.cpython-38.pyc | Bin 0 -> 7307 bytes A_core/settings.py | 8 + A_core/sms_utils.py | 78 ++++- A_core/telegram_utils.py | 271 ++++++++++++++++++ B_main/__pycache__/forms.cpython-38.pyc | Bin 8154 -> 8183 bytes B_main/__pycache__/urls.cpython-38.pyc | Bin 780 -> 836 bytes B_main/__pycache__/views.cpython-38.pyc | Bin 8405 -> 9432 bytes B_main/forms.py | 2 +- B_main/templates/B_main/main.htm | 2 +- B_main/templates/B_main/partials/card.htm | 103 ++++--- B_main/templates/B_main/signup.html | 6 + B_main/templates/B_main/test_telegram.html | 78 +++++ B_main/urls.py | 1 + B_main/views.py | 56 +++- C_accounts/__pycache__/views.cpython-38.pyc | Bin 8649 -> 8864 bytes C_accounts/views.py | 13 +- db.sqlite3 | Bin 512000 -> 614400 bytes templates/account/login.html | 2 +- 20 files changed, 556 insertions(+), 64 deletions(-) create mode 100644 A_core/__pycache__/telegram_utils.cpython-38.pyc create mode 100644 A_core/telegram_utils.py create mode 100644 B_main/templates/B_main/test_telegram.html diff --git a/A_core/__pycache__/settings.cpython-38.pyc b/A_core/__pycache__/settings.cpython-38.pyc index 7c1b3c8067c410a54fbaf1602be76c37414e3ca2..fa057f36ec5d7f4ae06a395a13615e67a5bac1c4 100644 GIT binary patch delta 748 zcmYjM$x>QT5Pbs`6d#E50M7HkpbP?zC^!TG1su{Dp6GdAFd#&zvciT%R#{r^5>pko z*}BVLWRt3_vd$M|o9nAoD)-jyKIe3wzSUpH-cMK#%x06o&-Wi+a^w9s7Q56b-OR@7 zMpDo~W;BuoCe8d&K+`9gnxVi93z}g?3wuIVw4x2|=s+jB(9Mk0f*$n320QxDj{yu~ z2*Vh`C{w5vW7LLmOfZSsF^MTmV+ONuzzG-Jn8Q44q7HbdvktG;2%k-0BK%lD06~PX z$P9TAOLqn)QWut~8!K4leQP!W;X5fWQV-U#!F$#b(PLDPn|YBLs25vg!#3IBr#}Ar zu|osg{nRGVAkVSK-)IQCh_kvx!${Bwmy^6aiai>`K8+(q6G-PonnW5I959)t_^4?Z zX$EPU)#T3(9OCF+BBzdz@r+}ajtS2>cI)^BUUEE#S2WN49`5%-B_Cht1X-M>a0WoT zlW=~=cP78KpxYR7_tHC&0)KfP(0LFS6vAs-M1hw0z-1I^1(&qS^BUx|FjabRN*~0! z)8`I){T@#s;17kvo9f0}Pch=~T)m0y$Ink$`7r4TUL+`ToYY>OpGv;mE>dn;;5;K~cm8QIV~(Vl>G56%C?t`>o~IAH3kwMgRZ+ delta 516 zcmXYuNmEln5XUp`y)qD9783S^u!ax_0m2>@cSKR6A};L~^%p1658%bB96WiXxL8&W zaP*v;-@y;!H}GWHzH+Lj`#0VF@0qUolK+v59Y>>)kglJfzBj&RPGTA7k2xtGwz}vh zLJvMsdqI3+#7WRgk`#S5EK&LyV2~k(86i!EQL>CNZheU{A#o;|vW_H}CdUkUW+^bo zJVh2L*&gX-QIagxyID?$tf9;btE{okhV@!a!)DvFMpA4^9~HK>a3>vNx9v2I^s~oB zt=Z#JU@r&uO4C?R2DmDNT$3TnGOT-q>ylRWemW!>^*8=qC!-v2Qy-Y+mW*liP_yIQ zmI?02B(1wLMYUmMnkx6WZ(Yf0s~N(Qrz*2`_pHDJj?NvK3-}?&%JTs~;<0iu;3qs) zUO=Rz`bE_*@l2L=py#~cC9kOQy6y0$?de$M`bwaBTY+=uvys*RfjYI|yv93O$I1o` z+0=$xG^N6O*;c=UdlV{s=jd*$xAJ5Q}iIsU|u|J}q?YC86UUrt2diDCc91?5~K z?w?4+&nC|+v9LTDiJwjQljoA{U7)xk51g8_8n*| zjKa#S%+2vLoXX(|g_HR?mSvz9tlptAOH7HgJg3Uqyf*5|u2Mb5>}9vyF1uA$?kEaD zs^p;xvS>!uRV8JZpA>9T`xJiCVe`G`i#m48M!OS^!W5& zyhpTcF*q0uhx&t|@ZfM?|G@9B9Qz+v$`7CD*H7|!GzPM$I-et%N;R{IjJuy1dT|_) z(k8iGGQLPx3oy7^)X3%K2R<`MUqd8aAlY(lHh@yK@%+`6C>TFnTVF!PN}gnkkBQL*jb%LWTR-5Zy5^(ay?BlUs}>V0)7d^@N;0c-AXK}gr^g748H+>>L8f0zW&!n zUx!U*E2LaRkX1j_c|jcNabbxDa$xjZozET+Fnv!ma9d1HC&Nv#0X-u6bkg~_)(uM- z!NUohi*>OQ++NnjbF6v);dDI&KHQv#l>k<%Hv%R~lhiK(9%wrOE%_dpU5HQ0ya}yP z;!Tzub+Dm7NofVp>TrSQYT@181P|uSP++|Snx_aLDtuX}VG2-+0Hvf4cOBL6Td<7* z{3h7c+hB}rfs_iUdLu`c7OHdU>WAy*31q$oo_3!A+WBhJxbiVsOw$u4H&&{P>*Uid zGM^`xifCtbfn2;-{m6D?c%0^W9*jk< zfZSXNAZt)>fi#pFf~9+dW|P-So0dMjfStfSiCf{D#NjaL5A`^jC}ElrpP?oM2Jkeg zI!Om2)KF0IBMq}3o-}jmeRxV90K;&=7wZAOYJ^Fwi!=H9)9Ex<;!#)><>^xEazIa; z$y5a%Y)tVgkFP=Le5THo$^kyYj1c!&BbozH6Mz?|g<6}{yIM$tifQAnU2POc5Pu({ zJ51q59i|$xM;4(;cJ@?mnU=(TK%dvFUse6Lu3pj3;4wWTz1(*39x0XsNKZ?BPRh{Y z=lb_jx3-&}e}ZRcZPnO-($RzmN8#&W8H`!(ek$cqeUX*#U!aM@kD(D_>i!pKa11j3 z9Vn+^4j5{Ja*FP;MvMNcZ~tNXN0@vXObrcz?E*xacnDC2nuzEVzSA9NU@Z<%Goe4> PeNz9~cZfgu*u#GT*rcEC delta 528 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 literal 0 HcmV?d00001 diff --git a/A_core/settings.py b/A_core/settings.py index a17d5ea..20b61e5 100644 --- a/A_core/settings.py +++ b/A_core/settings.py @@ -30,6 +30,14 @@ SECRET_KEY = 'django-insecure-kst@+h&50%!m$(d!l*qbb0l7f@z#@#me__yye^$5kg%0m%1=im # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False +# SMS 테스트 모드 설정 +SMS_TEST_MODE = False # True: 테스트 모드 (콘솔 출력), False: 실제 SMS 발송 + +# 텔레그램 봇 설정 +TELEGRAM_BOT_TOKEN = "5094633886:AAF_Cy3mD-3rqKQMfbgpVO39jNZDXNFZN-o" # BotFather에서 받은 토큰 +TELEGRAM_CHAT_ID = "5085456424" # getUpdates로 얻은 chat_id +TELEGRAM_API_URL = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage" + ALLOWED_HOSTS = ['www.sillaamp.com', 'sillaamp.com', '192.168.1.119', 'localhost', '127.0.0.1', '*'] CSRF_TRUSTED_ORIGINS = [ 'http://www.sillaamp.com', diff --git a/A_core/sms_utils.py b/A_core/sms_utils.py index c3dec49..85bb228 100644 --- a/A_core/sms_utils.py +++ b/A_core/sms_utils.py @@ -144,23 +144,81 @@ sms_service = NaverCloudSMS() def send_verification_sms(phone_number: str, verification_code: str) -> Dict[str, Any]: """인증번호 SMS 발송 함수 (편의 함수)""" - # 실제 SMS 발송 시도 - print(f"[DEBUG] SMS 발송 시도: {phone_number} - {verification_code}") - print(f"[DEBUG] Access Key: {sms_service.access_key}") - print(f"[DEBUG] Service ID: {sms_service.service_id}") - print(f"[DEBUG] Sender Phone: {sms_service.sender_phone}") - result = sms_service.send_verification_code(phone_number, verification_code) - print(f"[DEBUG] SMS 발송 결과: {result}") + # SMS 테스트 모드 확인 + sms_test_mode = getattr(settings, 'SMS_TEST_MODE', False) - return result + if sms_test_mode: + # 테스트 모드: 콘솔에 인증번호 출력 + print("=" * 60) + print("🔔 [SMS 테스트 모드] 인증번호 발송") + print(f"📱 수신번호: {phone_number}") + print(f"🔑 인증번호: {verification_code}") + print(f"📝 메시지: [신라AMP] 인증번호는 [{verification_code}]입니다. 3분 이내에 입력해주세요.") + print("=" * 60) + + return { + 'success': True, + 'message': 'SMS 테스트 모드: 콘솔에 인증번호가 출력되었습니다.', + 'test_mode': True, + 'verification_code': verification_code # 테스트 모드에서만 포함 + } + else: + # 실제 SMS 발송 모드 + print(f"[DEBUG] 실제 SMS 발송 시도: {phone_number} - {verification_code}") + print(f"[DEBUG] Access Key: {sms_service.access_key}") + print(f"[DEBUG] Service ID: {sms_service.service_id}") + print(f"[DEBUG] Sender Phone: {sms_service.sender_phone}") + + result = sms_service.send_verification_code(phone_number, verification_code) + print(f"[DEBUG] SMS 발송 결과: {result}") + + return result def send_withdrawal_approval_sms(phone_number: str, name: str) -> Dict[str, Any]: """탈퇴 승인 SMS 발송 함수 (편의 함수)""" - return sms_service.send_withdrawal_approval_sms(phone_number, name) + sms_test_mode = getattr(settings, 'SMS_TEST_MODE', False) + + if sms_test_mode: + message = f"[신라AMP] {name}님의 회원탈퇴가 승인되었습니다. 그동안 이용해주셔서 감사했습니다." + print("=" * 60) + print("🔔 [SMS 테스트 모드] 탈퇴 승인 알림") + print(f"📱 수신번호: {phone_number}") + print(f"👤 대상자: {name}") + print(f"📝 메시지: {message}") + print("=" * 60) + return { + 'success': True, + 'message': 'SMS 테스트 모드: 탈퇴 승인 알림이 콘솔에 출력되었습니다.', + 'test_mode': True + } + else: + return sms_service.send_withdrawal_approval_sms(phone_number, name) def send_withdrawal_rejection_sms(phone_number: str, name: str, reason: str = None) -> Dict[str, Any]: """탈퇴 거부 SMS 발송 함수 (편의 함수)""" - return sms_service.send_withdrawal_rejection_sms(phone_number, name, reason) + sms_test_mode = getattr(settings, 'SMS_TEST_MODE', False) + + if sms_test_mode: + if reason: + message = f"[신라AMP] {name}님의 회원탈퇴 요청이 거부되었습니다. 사유: {reason}" + else: + message = f"[신라AMP] {name}님의 회원탈퇴 요청이 거부되었습니다. 자세한 사항은 관리자에게 문의해주세요." + + print("=" * 60) + print("🔔 [SMS 테스트 모드] 탈퇴 거부 알림") + print(f"📱 수신번호: {phone_number}") + print(f"👤 대상자: {name}") + print(f"📝 메시지: {message}") + if reason: + print(f"❌ 거부 사유: {reason}") + print("=" * 60) + return { + 'success': True, + 'message': 'SMS 테스트 모드: 탈퇴 거부 알림이 콘솔에 출력되었습니다.', + 'test_mode': True + } + else: + return sms_service.send_withdrawal_rejection_sms(phone_number, name, reason) diff --git a/A_core/telegram_utils.py b/A_core/telegram_utils.py new file mode 100644 index 0000000..54bece8 --- /dev/null +++ b/A_core/telegram_utils.py @@ -0,0 +1,271 @@ +""" +텔레그램 봇 유틸리티 +회원가입 알림 등을 위한 텔레그램 메시지 전송 기능 +""" + +import requests +import threading +from django.conf import settings +from datetime import datetime + + +def get_client_ip(request): + """ + 클라이언트의 실제 IP 주소를 추출 + 프록시, 로드밸런서, CDN 등을 고려하여 실제 IP를 찾음 + """ + # 프록시나 로드밸런서를 통한 실제 클라이언트 IP 확인 + x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') + if x_forwarded_for: + # 첫 번째 IP가 실제 클라이언트 IP (쉼표로 구분된 경우) + ip = x_forwarded_for.split(',')[0].strip() + if ip and ip != '127.0.0.1': + return ip + + # Cloudflare 등 CDN에서 사용하는 헤더 + cf_connecting_ip = request.META.get('HTTP_CF_CONNECTING_IP') + if cf_connecting_ip and cf_connecting_ip != '127.0.0.1': + return cf_connecting_ip + + # Nginx 등 리버스 프록시에서 사용 + x_real_ip = request.META.get('HTTP_X_REAL_IP') + if x_real_ip and x_real_ip != '127.0.0.1': + return x_real_ip + + # 기본 REMOTE_ADDR + remote_addr = request.META.get('REMOTE_ADDR', '알 수 없음') + + # 로컬 환경 표시 + if remote_addr == '127.0.0.1': + return f"{remote_addr} (로컬 개발환경)" + elif remote_addr.startswith('192.168.') or remote_addr.startswith('10.') or remote_addr.startswith('172.'): + return f"{remote_addr} (내부 네트워크)" + + return remote_addr + + +def get_device_info(user_agent): + """User-Agent에서 기기 정보 추출""" + if not user_agent: + return "알 수 없음" + + # 모바일 기기 체크 + if any(mobile in user_agent for mobile in ['iPhone', 'iPad', 'Android', 'Mobile']): + if 'iPhone' in user_agent: + return "iPhone" + elif 'iPad' in user_agent: + return "iPad" + elif 'Android' in user_agent: + if 'Mobile' in user_agent: + return "Android 폰" + else: + return "Android 태블릿" + else: + return "모바일" + + # 데스크톱 OS 체크 + if 'Windows NT' in user_agent: + if 'Windows NT 10.0' in user_agent: + return "Windows 10/11" + elif 'Windows NT 6.3' in user_agent: + return "Windows 8.1" + elif 'Windows NT 6.1' in user_agent: + return "Windows 7" + else: + return "Windows" + elif 'Macintosh' in user_agent or 'Mac OS X' in user_agent: + return "Mac" + elif 'Linux' in user_agent and 'Android' not in user_agent: + return "Linux" + + return "알 수 없음" + + +def get_browser_info(user_agent): + """User-Agent에서 브라우저 정보 추출""" + if not user_agent: + return "알 수 없음" + + # 브라우저 체크 (순서 중요 - Chrome이 Safari 문자열도 포함하므로) + if 'Edg/' in user_agent: + return "Microsoft Edge" + elif 'Chrome/' in user_agent and 'Safari/' in user_agent: + return "Chrome" + elif 'Firefox/' in user_agent: + return "Firefox" + elif 'Safari/' in user_agent and 'Chrome' not in user_agent: + return "Safari" + elif 'Opera' in user_agent or 'OPR/' in user_agent: + return "Opera" + + return "알 수 없음" + + +def send_telegram_message(message, chat_id=None): + """ + 텔레그램 봇으로 메시지 전송 + + Args: + message (str): 전송할 메시지 + chat_id (str, optional): 채팅 ID. 없으면 기본 설정값 사용 + + Returns: + dict: {'success': bool, 'error': str} + """ + try: + if not hasattr(settings, 'TELEGRAM_BOT_TOKEN') or not settings.TELEGRAM_BOT_TOKEN: + return {'success': False, 'error': 'TELEGRAM_BOT_TOKEN이 설정되지 않았습니다.'} + + if not chat_id: + chat_id = getattr(settings, 'TELEGRAM_CHAT_ID', None) + if not chat_id: + return {'success': False, 'error': 'TELEGRAM_CHAT_ID가 설정되지 않았습니다.'} + + url = f"https://api.telegram.org/bot{settings.TELEGRAM_BOT_TOKEN}/sendMessage" + + payload = { + 'chat_id': chat_id, + 'text': message, + 'parse_mode': 'HTML' # HTML 포맷 지원 + } + + response = requests.post(url, json=payload, timeout=10) + + if response.status_code == 200: + result = response.json() + if result.get('ok'): + print(f"[TELEGRAM_SUCCESS] 메시지 전송 성공: {message[:50]}...") + return {'success': True, 'error': None} + else: + error_msg = result.get('description', '알 수 없는 오류') + print(f"[TELEGRAM_ERROR] API 오류: {error_msg}") + return {'success': False, 'error': f'텔레그램 API 오류: {error_msg}'} + else: + print(f"[TELEGRAM_ERROR] HTTP 오류: {response.status_code}") + return {'success': False, 'error': f'HTTP 오류: {response.status_code}'} + + except requests.exceptions.Timeout: + print("[TELEGRAM_ERROR] 요청 시간 초과") + return {'success': False, 'error': '요청 시간 초과'} + except requests.exceptions.RequestException as e: + print(f"[TELEGRAM_ERROR] 네트워크 오류: {str(e)}") + return {'success': False, 'error': f'네트워크 오류: {str(e)}'} + except Exception as e: + print(f"[TELEGRAM_ERROR] 예상치 못한 오류: {str(e)}") + return {'success': False, 'error': f'예상치 못한 오류: {str(e)}'} + + +def send_telegram_message_async(message, chat_id=None): + """ + 비동기로 텔레그램 메시지 전송 (백그라운드) + + Args: + message (str): 전송할 메시지 + chat_id (str, optional): 채팅 ID + """ + def _send(): + send_telegram_message(message, chat_id) + + thread = threading.Thread(target=_send) + thread.daemon = True + thread.start() + + +def send_signup_notification(name, phone, request): + """ + 회원가입 문자인증 요청 알림 + + Args: + name (str): 가입자 이름 + phone (str): 전화번호 + request: Django request 객체 (IP, User-Agent 정보) + """ + current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # 클라이언트 정보 추출 + ip = get_client_ip(request) + user_agent = request.META.get('HTTP_USER_AGENT', '알 수 없음') + device_info = get_device_info(user_agent) + browser_info = get_browser_info(user_agent) + + # 디버그 정보 출력 + print(f"[TELEGRAM_DEBUG] 회원가입 알림 - IP: {ip}, 기기: {device_info}, 브라우저: {browser_info}") + + # 상세 헤더 정보 (개발 환경에서만) + if ip.endswith('(로컬 개발환경)'): + print(f"[DEBUG] REMOTE_ADDR: {request.META.get('REMOTE_ADDR')}") + print(f"[DEBUG] HTTP_X_FORWARDED_FOR: {request.META.get('HTTP_X_FORWARDED_FOR')}") + print(f"[DEBUG] HTTP_X_REAL_IP: {request.META.get('HTTP_X_REAL_IP')}") + print(f"[DEBUG] HTTP_CF_CONNECTING_IP: {request.META.get('HTTP_CF_CONNECTING_IP')}") + print(f"[DEBUG] User-Agent: {user_agent[:100]}...") + + message = f""" +🔔 신라AMP 회원가입 알림 + +👤 이름: {name} +📱 전화번호: {phone} +⏰ 요청시간: {current_time} + +🌐 접속 정보: +• IP 주소: {ip} +• 기기: {device_info} +• 브라우저: {browser_info} + +💡 새로운 회원가입 요청이 있습니다! + """.strip() + + # 비동기로 전송 (사용자 대기시간 없음) + send_telegram_message_async(message) + + +def send_password_reset_notification(phone, request): + """ + 비밀번호 찾기 문자인증 요청 알림 + + Args: + phone (str): 전화번호 + request: Django request 객체 (IP, User-Agent 정보) + """ + current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # 클라이언트 정보 추출 + ip = get_client_ip(request) + user_agent = request.META.get('HTTP_USER_AGENT', '알 수 없음') + device_info = get_device_info(user_agent) + browser_info = get_browser_info(user_agent) + + # 디버그 정보 출력 + print(f"[TELEGRAM_DEBUG] 비밀번호 찾기 알림 - IP: {ip}, 기기: {device_info}, 브라우저: {browser_info}") + + message = f""" +🔑 신라AMP 비밀번호 찾기 알림 + +📱 전화번호: {phone} +⏰ 요청시간: {current_time} + +🌐 접속 정보: +• IP 주소: {ip} +• 기기: {device_info} +• 브라우저: {browser_info} + +💡 비밀번호 찾기 요청이 있습니다! + """.strip() + + # 비동기로 전송 (사용자 대기시간 없음) + send_telegram_message_async(message) + + +def test_telegram_bot(): + """ + 텔레그램 봇 연결 테스트 + """ + test_message = f"🧪 신라AMP 봇 테스트\n\n⏰ 테스트 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n✅ 봇이 정상적으로 작동하고 있습니다!" + + result = send_telegram_message(test_message) + + if result['success']: + print("✅ 텔레그램 봇 테스트 성공!") + return True + else: + print(f"❌ 텔레그램 봇 테스트 실패: {result['error']}") + return False diff --git a/B_main/__pycache__/forms.cpython-38.pyc b/B_main/__pycache__/forms.cpython-38.pyc index 76afad112dd0afea0f53a1bc7c30e4aee9d6bd20..9aa304e778685693c60dcac3d74b4445d6d69621 100644 GIT binary patch delta 96 zcmV-m0H6QbKleWkRSgXa000006WOY0o3Ra59YjF@008Q;fb4^bAnk&r?3;}1lbj$Z z?3jo;Ancx)?1P=^xqvJn>a(crl!WZEoa~;gEFkNQgzSo>>!`Twi=ZGbE-tf)9Z>=m C+Ag*L delta 67 zcmexvf6JaXl$V!_0SFG|ugs|1$QvrFt^(w}+SKr-xl`e7(~>u{dtOhTqoDC-Mu(Nc W+gUxYHn+UmFyT$pn$62(1DODX=^k_d diff --git a/B_main/__pycache__/urls.cpython-38.pyc b/B_main/__pycache__/urls.cpython-38.pyc index 4b02e015921a900b5f5dad4b25d270199ec36e2d..35406aa4b7d063984fa255fe77501b77927fcca3 100644 GIT binary patch delta 165 zcmeBSJHnJy685kaeILLq>$Z-JT;$0K9w{oR(NAagf2Qz5OZ2V!! z$n2*nFxj6eS?m^fX;Dr=Vo6DAQC{&%h9VK5h9Z&4XPLs~q<~BwMjmE9MlA3v0G2Ko As{jB1 diff --git a/B_main/__pycache__/views.cpython-38.pyc b/B_main/__pycache__/views.cpython-38.pyc index 5b8c4cd721a42e1354c6d960433b46e1abde33fd..dbd928a8556224c80cca2b10b1e4b7217e796263 100644 GIT binary patch delta 2534 zcmZ`)TTdHT5Z<%9HpbZ4fC1aF!K6UqkUE!2t<*+olajPaqpE6}htNhA_1O@>*l>28 zTaRNBLqh|toGz$IYKX0nt5B7zLe>0&K2~Z~`JoSyTB+>v(uY3urp{SkNKj&H$1`(g z=XSpF{J7)iJ+%kyb}I*;>B6leeY?)r`p}s>T_=%!1MV8*dS1r0`?=u8d$-VzdWRRx zs^NJP9(kXr{?*W#%9*hVoAZ3GD$n7nK|U!`ZomRs3+D5D(o~YFu@xjUNY%JzkS7aN zl%v#4qSSL0N@5x`38E3STm7QIkvXR$*(`OoM}J2-H$ z3ZR*A5y{39@5KmE(zH@#vY9s1CO3yW21N)y|70ZG36WApEz|{vzfl)$gmA!NztJ9m z2sZAU!A9|=xum<~z((DyKX@HppEa%Oh93tjCAd}s(i*%WD=^!bSFwXHJ3C928fz^d z0)zn18If$JHUQFIT5V8kMv$isY`a!tJ0py|`z&;?1gXU6Dklcz72%l@iA(W;$nfc= zZUcw{`3E?E5dnE2=}}q$BOZnkDliUj#4mmh$aro-zbLIZltvYll>4=*gc4u5n_CTN ztvY^YHLZ9va~RyB{?NScwStLAvZRog;IE`0DkLElYUKXDSYkLVE7EYmQ4)?yr2lZg zgm;s6u>73|qB`7N2Nht53?%V7kPo|e?L6?-p}_K`DgEor((O@wZakomE$ZVJ^x6A? zrQ0)lF019U0qxp?K0c{sF6duP=;t12lM~wHe25%l-i|YJR6XQ=#}`Bew=BhQxRgaK zu0R%%UZp=CQ*XGxt4T83Q;ek4m?xV`bOW!?F6!52SwQ{bn094qb(nzm?U;6ZRJ%K| zd|^?$`UokQ0%JB+KC;AiaH3 zVnZdOc2{lpc085S2j0_uBgZl3pyH)qE*2CFsJeGkCu=F`08%hT61vYYthE4cUYy zQ!T3Hy}SivC$b`|AR>!;%C}Jz;8?xod#{c+BQxU5-@h#u^^iJ?xpTlv;vufvk1tQ59^xxx)jCOr0py$pnJ$wW+ zHL9Jv8YF&jL0AzmGj)n2E8z-4K8`D-1x^xsfjrKRgXh%qV!BMoga_3oKup(PvYOPfZ)S44Iwu>0`P!M%bMPg(CV5OXH}E9d&JC zbR#3|t-1Yc3VZ#j)7)P1dQqR-S!>mC~$sDd~k$7AHY h`fKYkXBHG>mJtRha#`&QbfVqr7lCe5hefoQ{s)yi34;It delta 1525 zcmZ`(-D@0G6rVe@AG5PF`#Jl)*_dpabQ@9ZgQ&!Z+E5xqDHb0TB&0o)ny>x1Gl^|_ z$8Fi#r;3q_t;&Ks2nFBN77=`rKKKVnl`;>4FM|Jt=g#gX+fWzo@1A?kJzu|jW}mG5 zaVGJuVQ3QmzIML*F!lQ9i7MIuVR4^?6Bnh%sRuujGnr%_%_^?5xa`LYm(BSugtugR ziAkH0K@=h$u`fY%S^6O3h60_+LLhlEkvgw|EHt1lNe~O9$dWYjQ$hA*H#|CN9&sZh zIgWDF3GQQj-6K8Xsv}zjVqRBvV=N5vmV6-7pU@BnBar)!hNn1Q4|a4;V5m5YLlW(T z`b-<>o`li?(>4uAl_X3mq%=xVC&9=d2^q+GGRDxI6pJ#wC%XnSz$^*Bt4wE77dz6i zkDy-AP>PD`AU@fplXfzLj3@O-V9PB@PL|=#9`fRtOoBXeqd@_RQ0j+z%Ed`-49ZX$ zYMbYw>L#$r9HxIH_CYuANl16uO|m2^@3SNjtUp1p!pSmH3$ugJvIh_xXT zGQA!2UqkaG5E~nDCT1|5LVsp4 zO#eHJ1Q}oKe=J5eNkUM`qYeX^0@f;_{g^Bkc4xB>!P7%8i17|Wb7%hd~LaBta zfivMLZq6y=tLMb4Get-+4-20vMEakCl+HTxa&m%~8D@%T&=}1_)p@mu)s5*nW`1Tj zBcdCp_&ddgJe|W&*-zRVSDL%+)jf-DtZ!Jr9~Kwu9bt)&EVg5{=`L!{Q5%su0w{l_ z^d|qN^h^AjIPMF$&aHCa_*X{Rj|`o;T{->KjqfWr3&O(>uea#VY5JaUyo%7M@VBck zX6MCuLBI(FUun16{M+ieE*(a-C~Z;OkL;~&?^-rh#6?5EWdQ|*MxLG&@+|>v0Vaa4 zY+2W6Q0ieudR-XQapQDNT;3D9(B&3bzGm;(tJJc0w;jJ`1esTNEZbhYV!f{;Rok#S zEQfx~A5Xmy=?VW?PSROksikvI@F)?L=tK|YaZeGAX#B0(^W3dnP>3q?ziKPwE`KR$ z@}=n;#uo(tSVfnTOz)v~ivKhHAoJCsR-s>_t?@GdZT2SlgrBdUCAayV`id?(^+TI> jt4Z(hzv~~R?xKR;6Cgb3Vo=hJxdd6}pUo{Rx*GZqk7J35 diff --git a/B_main/forms.py b/B_main/forms.py index dbfee55..7ff220e 100644 --- a/B_main/forms.py +++ b/B_main/forms.py @@ -305,6 +305,6 @@ class PersonForm(forms.ModelForm): }), 'keyword1': forms.TextInput(attrs={ 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', - 'placeholder': '검색 키워드 (예: 회계감사)' + 'placeholder': '검색 키워드 (예: 잘생김, 골프천재, 댄스머신 ...)' }), } diff --git a/B_main/templates/B_main/main.htm b/B_main/templates/B_main/main.htm index 4aa421a..9669fd2 100644 --- a/B_main/templates/B_main/main.htm +++ b/B_main/templates/B_main/main.htm @@ -304,7 +304,7 @@ // 소개글 설정 if (intro && intro.trim() !== '') { - modalIntro.textContent = intro; + modalIntro.innerHTML = intro.replace(/\n/g, '
'); modalIntroSection.classList.remove('hidden'); modalNoIntro.classList.add('hidden'); } else { diff --git a/B_main/templates/B_main/partials/card.htm b/B_main/templates/B_main/partials/card.htm index acb1baa..9e8d894 100644 --- a/B_main/templates/B_main/partials/card.htm +++ b/B_main/templates/B_main/partials/card.htm @@ -1,34 +1,38 @@ {% load static %} -
+
-
- {% if person.사진 and person.사진.url and 'media/' in person.사진.url %} - {{ person.이름 }} - {% else %} - {{ person.이름 }} - {% endif %} - {% if person.이름 %} - + + {% if person.이름 %} + {% endif %}
-
+

@@ -55,27 +59,40 @@ {% endif %}

-
-
- 소속: - {{ person.소속 }} +
+
+
+ 소속: + {{ person.소속 }} +
+
+ 직책: + {{ person.직책 }} +
+
+ 연락처: + {{ person.연락처 }} +
+
+ {% if person.이름 == '허남식' %} + 이메일: + {% else %} + 주소: + {% endif %} + {{ person.주소 }} +
-
- 직책: - {{ person.직책 }} -
-
- 연락처: - {{ person.연락처 }} -
-
- {% if person.이름 == '허남식' %} - 이메일: - {% else %} - 주소: - {% endif %} - {{ person.주소 }} + + + {% if person.소개글 %} +
+
+
+ {{ person.소개글 }} +
+
+ {% endif %}
\ No newline at end of file diff --git a/B_main/templates/B_main/signup.html b/B_main/templates/B_main/signup.html index 6a92014..847c0dd 100644 --- a/B_main/templates/B_main/signup.html +++ b/B_main/templates/B_main/signup.html @@ -30,6 +30,12 @@ {% if message %}
{{ message }}
{% endif %} + + {% if success_message %} +
+ {{ success_message }} +
+ {% endif %} {% if step == 1 %}
diff --git a/B_main/templates/B_main/test_telegram.html b/B_main/templates/B_main/test_telegram.html new file mode 100644 index 0000000..6823845 --- /dev/null +++ b/B_main/templates/B_main/test_telegram.html @@ -0,0 +1,78 @@ + + + + + + 텔레그램 봇 테스트 | 신라 AMP + + + + + + +
+
+

📱 텔레그램 봇 테스트

+

관리자 전용 기능

+
+ + + {% if messages %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endif %} + +
+
+

🤖 봇 정보

+
+

토큰: {{ settings.TELEGRAM_BOT_TOKEN|slice:":10" }}...

+

채팅 ID: {{ settings.TELEGRAM_CHAT_ID }}

+
+
+ + + {% csrf_token %} + + + + +
+
+ + + + diff --git a/B_main/urls.py b/B_main/urls.py index 7fa94c4..5dc52f4 100644 --- a/B_main/urls.py +++ b/B_main/urls.py @@ -13,4 +13,5 @@ urlpatterns = [ path('signup/', views.signup_view, name='signup'), path('privacy-policy/', views.privacy_policy, name='privacy_policy'), path('test-500/', views.test_500_error, name='test_500_error'), + path('test-telegram/', views.test_telegram_bot, name='test_telegram_bot'), ] \ No newline at end of file diff --git a/B_main/views.py b/B_main/views.py index 9823de4..4cda177 100644 --- a/B_main/views.py +++ b/B_main/views.py @@ -268,13 +268,26 @@ def signup_view(request): from .forms import is_allowed_person from django.contrib.auth import login + # 강제 리셋 파라미터 확인 (로그인 페이지에서 오는 경우) + force_reset = request.GET.get('reset', '').lower() == 'true' + # GET 요청 시 세션 초기화 (새로운 회원가입 시작) # 단, 인증번호 확인 후 리다이렉트된 경우는 세션 유지 - if request.method == 'GET' and not request.session.get('signup_verified'): - for key in ['signup_code', 'signup_name', 'signup_phone', 'signup_verified', 'signup_step']: - request.session.pop(key, None) - request.session['signup_step'] = 1 - request.session['signup_verified'] = False + # 또한 step이 2이고 verified가 True인 경우도 세션 유지 + current_step = request.session.get('signup_step', 1) + current_verified = request.session.get('signup_verified', False) + + if request.method == 'GET' and (force_reset or not (current_verified and current_step == 2)): + # 강제 리셋이거나 인증되지 않았거나 step이 2가 아닌 경우에만 세션 초기화 + if force_reset or not current_verified: + for key in ['signup_code', 'signup_name', 'signup_phone', 'signup_verified', 'signup_step']: + request.session.pop(key, None) + request.session['signup_step'] = 1 + request.session['signup_verified'] = False + + # 강제 리셋인 경우 디버그 메시지 출력 + if force_reset: + print("[DEBUG] 회원가입 세션이 강제로 리셋되었습니다.") step = request.session.get('signup_step', 1) name = request.session.get('signup_name') @@ -308,6 +321,10 @@ def signup_view(request): # 전화번호 인증 로그 기록 log_phone_verification(request, phone) + # 텔레그램 알림 전송 (비동기) + from A_core.telegram_utils import send_signup_notification + send_signup_notification(name, phone, request) + return render(request, 'B_main/signup.html', { 'step': 1, 'form1': form, 'code_sent': True, 'message': '인증번호가 발송되었습니다.' }) @@ -349,7 +366,15 @@ def signup_view(request): # 인증 성공 request.session['signup_verified'] = True request.session['signup_step'] = 2 - return redirect('signup') + # 인증 성공 메시지와 함께 2단계로 직접 이동 + form2 = Step2AccountForm() + return render(request, 'B_main/signup.html', { + 'step': 2, + 'form2': form2, + 'name': name, + 'phone': phone, + 'success_message': '인증이 완료되었습니다. 비밀번호를 설정해주세요.' + }) else: return render(request, 'B_main/signup.html', { 'step': 1, 'form1': form, 'code_sent': True, 'error': '인증번호가 올바르지 않습니다.' @@ -409,4 +434,21 @@ def privacy_policy(request): def test_500_error(request): """500 에러 페이지 테스트용 뷰""" # 강제로 에러를 발생시킵니다 - raise Exception("500 에러 페이지 테스트를 위한 의도적인 에러입니다.") \ No newline at end of file + raise Exception("500 에러 페이지 테스트를 위한 의도적인 에러입니다.") + +def test_telegram_bot(request): + """텔레그램 봇 테스트용 뷰 (관리자만 접근 가능)""" + if not request.user.is_superuser: + return redirect('/') + + from A_core.telegram_utils import test_telegram_bot + + if request.method == 'POST': + success = test_telegram_bot() + if success: + messages.success(request, '✅ 텔레그램 봇 테스트 성공! 메시지가 전송되었습니다.') + else: + messages.error(request, '❌ 텔레그램 봇 테스트 실패! 설정을 확인해주세요.') + return redirect('test_telegram_bot') + + return render(request, 'B_main/test_telegram.html') \ No newline at end of file diff --git a/C_accounts/__pycache__/views.cpython-38.pyc b/C_accounts/__pycache__/views.cpython-38.pyc index 26e1d0ae4350e11dc42a84ac811640ea1b6c104f..73fe5116d6eb2d741be630291f2d0dc55cd76146 100644 GIT binary patch delta 1139 zcmaKq-)|d55XX1+&KK7|&X@Rnj*}xk(=@GtRBCCVBz-|`s1#C!h@b@pStjeYc9L^* zcWI=shZS6@l@KCm5fTtf_Crb4m#Q^LJo3N`FNgzVe9{%$fEBlvu^d*@2={5^fz&`sPPz;ym$!J{>ejpj(0Kxu*{saR6-v7KUO zhlY9(4-)M}r$Ml?6i;z|m@s=u#p-@x&!NODPw-@4BE|;gQ8sXzu(7@crwNvp=jwi0 zfzc$P5nvp1Fvj32D{L!GWuCC-F_+2?BDFxpsOaM;%A=g}NP}x^d^;jTIj}JlRJ==E z!Ew%Vb&L96GrBLyv?pa8I!it@!+u%qAwI~I4$f?guHvwtmS7s2V9FDP{mW9kxxm0| zO7kRy1~>ZmqIj=x0|$2)2RF3wUXZStHNZ}ed=6onr`s7m)WIu2%MvtwsF7_CW4}cy z9pu;xO}IejiAwO?$Wjd3I1WUp}HWb+H^&>#jG0fud$kia$-;n8l6vro!roIS2-F~mMfRN@=9qfs4n~7 zip-)*P>GW8t%Gqzn&XI~DYmUsup)l9hP8{*ds+N#9fLoFlbw#3N&*sMAv>0$x}t#w zl;Ha?s){hX1gqV@vMqo)aU+)pue+1`8K5q%jP5`n40{}T(w=~_Sg>D)_3oCPQsNs@ z;h4n7;!gf;TkdzE;+>MNK3Gx^P26>E<*&+5Qxa8)RS8dGU1C%C Vg$Xz+zAcO=8)Se&%)~W8;Su!FDklH{ delta 930 zcmZva&r4KM6vyv*zh<1@&R0B52VfT11=L^e?r9{fAo>va`x}1| zYH2$5tmh>E96D0Q@t5IMy`P^6C(iY&gS(zMKa5NSO<8=F1QrB*d?4DXWkf-U&qaq4OCn1J@&dO6 zmIY>cJvykp!H~#&$KT4Gv6wp|DWFRlxWL6<`dj#PY#G+-uVM!P7x-MF4c6-{aR^}W z^U23h;$M>)q$`z$X+D@b4V(3R$}hXOM1w(rZNA%n4Yv6E_K3QN37da!AAvpIot}dY nemDK7ttiTd1y%&s1l9#M1@`!*jx6-?Vn@ckBY{iDm6hNZL{sJk diff --git a/C_accounts/views.py b/C_accounts/views.py index f6b1458..3f0c76e 100644 --- a/C_accounts/views.py +++ b/C_accounts/views.py @@ -233,6 +233,10 @@ def password_reset(request): message = '인증번호가 발송되었습니다.' code_sent = True print(f"[DEBUG] 비밀번호 찾기 SMS 발송 성공: {phone} - {verification_code}") + + # 텔레그램 알림 전송 (비동기) + from A_core.telegram_utils import send_password_reset_notification + send_password_reset_notification(phone, request) else: error = '인증번호 발송에 실패했습니다. 잠시 후 다시 시도해주세요.' print(f"[DEBUG] 비밀번호 찾기 SMS 발송 실패: {sms_result['error']}") @@ -252,7 +256,14 @@ def password_reset(request): elif input_code == stored_code: request.session['password_reset_verified'] = True request.session['password_reset_step'] = 2 - return redirect('accounts:password_reset') + + print(f"[DEBUG] 비밀번호 찾기 인증 성공! 2단계로 직접 이동: {phone}") + + # 리다이렉트 대신 직접 2단계 렌더링 + form2 = ForcePasswordSetForm() + return render(request, 'C_accounts/password_reset.html', { + 'step': 2, 'form2': form2, 'phone': phone + }) else: error = '인증번호가 일치하지 않습니다.' else: diff --git a/db.sqlite3 b/db.sqlite3 index 83b9ba49ca8e298514ba22fd1bc1129b365254bf..c0ca4c7d715bb3781260aeda7be82dc0cf4906bc 100644 GIT binary patch delta 67715 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 Z4&^xeuapMLQEzEjjzn@Usn51x?MqK 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 MG-`D77i7f$0k<>_VE_OC diff --git a/templates/account/login.html b/templates/account/login.html index 12ecd68..0de201b 100644 --- a/templates/account/login.html +++ b/templates/account/login.html @@ -97,7 +97,7 @@
{% if not request.session.authenticated and not user.is_authenticated %} 계정이 없으신가요? - 회원가입 + 회원가입 {% endif %}