diff --git a/A_core/__pycache__/telegram_utils.cpython-38.pyc b/A_core/__pycache__/telegram_utils.cpython-38.pyc index a50912c..defed30 100644 Binary files a/A_core/__pycache__/telegram_utils.cpython-38.pyc and b/A_core/__pycache__/telegram_utils.cpython-38.pyc differ diff --git a/A_core/settings.py b/A_core/settings.py index 20b61e5..1be6b5e 100644 --- a/A_core/settings.py +++ b/A_core/settings.py @@ -38,6 +38,9 @@ TELEGRAM_BOT_TOKEN = "5094633886:AAF_Cy3mD-3rqKQMfbgpVO39jNZDXNFZN-o" # BotFath TELEGRAM_CHAT_ID = "5085456424" # getUpdates로 얻은 chat_id TELEGRAM_API_URL = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage" +# 텔레그램 알림 전송 여부 설정 +TELEGRAM_NOTIFICATIONS_ENABLED = True # True: 텔레그램 알림 전송, False: 텔레그램 알림 비활성화 + 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/telegram_utils.py b/A_core/telegram_utils.py index 8f8a7be..428501d 100644 --- a/A_core/telegram_utils.py +++ b/A_core/telegram_utils.py @@ -163,6 +163,11 @@ def send_telegram_message_async(message, chat_id=None): message (str): 전송할 메시지 chat_id (str, optional): 채팅 ID """ + # 텔레그램 알림이 비활성화된 경우 전송하지 않음 + if not getattr(settings, 'TELEGRAM_NOTIFICATIONS_ENABLED', True): + print("[TELEGRAM_INFO] 텔레그램 알림이 비활성화되어 있습니다.") + return + def _send(): send_telegram_message(message, chat_id) diff --git a/B_main/admin.py b/B_main/admin.py index 106a807..41e7fe2 100644 --- a/B_main/admin.py +++ b/B_main/admin.py @@ -90,10 +90,10 @@ class PersonAdmin(admin.ModelAdmin): @admin.register(AccessLog) class AccessLogAdmin(admin.ModelAdmin): - list_display = ['timestamp', '사용자명', 'action_display', 'ip_address', 'description'] + list_display = ['timestamp', '사용자명', 'action_display', 'ip_address', '기기정보', '브라우저정보', 'description'] list_filter = ['action', 'timestamp', 'ip_address'] search_fields = ['user__username', 'person__이름', 'description', 'ip_address'] - readonly_fields = ['timestamp', 'user', 'person', 'action', 'description', 'ip_address', 'user_agent', 'session_key', 'metadata', '변경사항_상세보기'] + readonly_fields = ['timestamp', 'user', 'person', 'action', 'description', 'ip_address', 'user_agent', 'session_key', 'metadata', '변경사항_상세보기', '기기정보', '브라우저정보'] date_hierarchy = 'timestamp' ordering = ['-timestamp'] @@ -132,12 +132,52 @@ class AccessLogAdmin(admin.ModelAdmin): action_display.short_description = '활동' action_display.admin_order_field = 'action' + def 기기정보(self, obj): + if obj.metadata and 'device_info' in obj.metadata: + device_info = obj.metadata['device_info'] + # 기기별 아이콘 추가 + icons = { + 'iPhone': '📱', + 'iPad': '📱', + 'Android 폰': '📱', + 'Android 태블릿': '📱', + 'Windows 10/11': '💻', + 'Windows 8.1': '💻', + 'Windows 7': '💻', + 'Windows': '💻', + 'Mac': '💻', + 'Linux': '💻', + } + icon = icons.get(device_info, '🖥️') + return format_html('{} {}', icon, device_info) + return format_html('알 수 없음') + 기기정보.short_description = '기기' + + def 브라우저정보(self, obj): + if obj.metadata and 'browser_info' in obj.metadata: + browser_info = obj.metadata['browser_info'] + # 브라우저별 색상 추가 + colors = { + 'Chrome': '#4285f4', + 'Firefox': '#ff7139', + 'Safari': '#1b88ca', + 'Microsoft Edge': '#0078d4', + 'Opera': '#ff1b2d', + } + color = colors.get(browser_info, '#6c757d') + return format_html( + '{}', + color, browser_info + ) + return format_html('알 수 없음') + 브라우저정보.short_description = '브라우저' + fieldsets = ( ('기본 정보', { 'fields': ('timestamp', 'user', 'person', 'action', 'description') }), ('접속 정보', { - 'fields': ('ip_address', 'user_agent', 'session_key') + 'fields': ('ip_address', '기기정보', '브라우저정보', 'user_agent', 'session_key') }), ('상세 변경사항', { 'fields': ('변경사항_상세보기',), diff --git a/B_main/log_utils.py b/B_main/log_utils.py index 51219c8..4f1260c 100644 --- a/B_main/log_utils.py +++ b/B_main/log_utils.py @@ -6,13 +6,38 @@ from .models import AccessLog, Person def get_client_ip(request): - """클라이언트 IP 주소 가져오기""" + """ + 클라이언트의 실제 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() - else: - ip = request.META.get('REMOTE_ADDR') - return ip + 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_user_agent(request): @@ -20,6 +45,63 @@ def get_user_agent(request): return request.META.get('HTTP_USER_AGENT', '') +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 log_user_activity(request, action, description=None, user=None, metadata=None): """ 사용자 활동 로그 기록 @@ -49,10 +131,13 @@ def log_user_activity(request, action, description=None, user=None, metadata=Non metadata = {} # 요청 정보 추가 + user_agent = get_user_agent(request) metadata.update({ 'path': request.path, 'method': request.method, 'referer': request.META.get('HTTP_REFERER', ''), + 'device_info': get_device_info(user_agent), + 'browser_info': get_browser_info(user_agent), }) # 로그 생성 diff --git a/C_accounts/__pycache__/views.cpython-38.pyc b/C_accounts/__pycache__/views.cpython-38.pyc index 5d65620..b47fc80 100644 Binary files a/C_accounts/__pycache__/views.cpython-38.pyc and b/C_accounts/__pycache__/views.cpython-38.pyc differ diff --git a/db.sqlite3 b/db.sqlite3 index f0798e5..c9a0ad0 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ