from django.shortcuts import render, redirect from django.http import HttpResponse, JsonResponse from urllib.parse import unquote from .models import Person from django.db import models from django.contrib.auth.decorators import login_required from .forms import PersonForm, Step1PhoneForm, Step2AccountForm from .models import Person from django.shortcuts import get_object_or_404 from django.db.models import Q, Case, When, Value, IntegerField from django.contrib.auth import login, logout from A_core.sms_utils import send_verification_sms from .log_utils import log_signup, log_phone_verification, log_search, log_main_access, log_error import random import json import time def password_required(request): PASSWORD = '1110' # 실제 비밀번호 # 로그인이 된 사용자는 바로 메인 페이지로 리다이렉트 if request.user.is_authenticated: next_url = request.GET.get("next", "/") if not next_url: next_url = "/" return redirect(next_url) if request.method == "POST": entered_password = request.POST.get("password") if entered_password == PASSWORD: request.session["authenticated"] = True next_url = request.POST.get("next", "/") if not next_url: next_url = "/" return redirect(next_url) else: return render(request, "B_main/password.htm", {"error": "Incorrect password. Please try again."}) # GET 요청 시 비밀번호 입력 폼 렌더링 next_url = request.GET.get("next", "/") return render(request, "B_main/password.htm", {"next": next_url}) # 인증 검사 함수 def check_authentication(request): # 로그인이 된 사용자는 인증 통과 if request.user.is_authenticated: return None # 세션 인증이 된 사용자도 통과 if request.session.get("authenticated"): return None # 둘 다 안 된 경우에만 비밀번호 페이지로 리다이렉트 return redirect(f"/accounts/login/?next={request.path}") def main(request): auth_check = check_authentication(request) if auth_check: return auth_check # 메인 페이지 접속 로그 기록 log_main_access(request) # 현재 사용자의 Person 정보 가져오기 current_user_person = None if request.user.is_authenticated: try: current_user_person = Person.objects.get(user=request.user) except Person.DoesNotExist: pass # 기본 필터: 이름이 있는 사람들 base_filter = Person.objects.filter( 이름__isnull=False ).exclude( 이름__exact='' ) # 현재 사용자의 권한에 따라 추가 필터 적용 if request.user.is_superuser or current_user_person is None: # 모든 사람 표시 (필터 추가 없음) pass elif current_user_person and not current_user_person.모든사람보기권한: # 모든사람보기권한이 False인 경우 회원가입한 사람만 표시 base_filter = base_filter.filter(user__isnull=False) # 순서가 있는 항목을 먼저 보여주고, 나머지는 가나다순으로 정렬 people = base_filter.annotate( sequence_order=Case( When(SEQUENCE__isnull=True, then=Value(1)), default=Value(0), output_field=IntegerField(), ) ).order_by('sequence_order', 'SEQUENCE', '이름') return render(request, 'B_main/main.htm', {'people': people}) def search_people(request): auth_check = check_authentication(request) if auth_check: return auth_check query = request.GET.get('q', '') # 현재 사용자의 Person 정보 가져오기 current_user_person = None if request.user.is_authenticated: try: current_user_person = Person.objects.get(user=request.user) except Person.DoesNotExist: pass # 기본 필터: 모든 사람 base_filter = Person.objects.all() # 현재 사용자의 권한에 따라 추가 필터 적용 if request.user.is_superuser or current_user_person is None: # 모든 사람 표시 (필터 추가 없음) pass elif current_user_person and not current_user_person.모든사람보기권한: # 모든사람보기권한이 False인 경우 회원가입한 사람만 표시 base_filter = base_filter.filter(user__isnull=False) if query: # 이름, 소속, 직책, 키워드로 검색 # 순서가 있는 항목을 먼저 보여주고, 나머지는 가나다순으로 정렬 people = base_filter.filter( Q(이름__icontains=query) | Q(소속__icontains=query) | Q(TITLE__icontains=query) | Q(직책__icontains=query) | Q(keyword1__icontains=query) | Q(생년월일__icontains=query) ).filter( 이름__isnull=False ).exclude( 이름__exact='' ).annotate( sequence_order=Case( When(SEQUENCE__isnull=True, then=Value(1)), default=Value(0), output_field=IntegerField(), ) ).order_by('sequence_order', 'SEQUENCE', '이름') else: # 순서가 있는 항목을 먼저 보여주고, 나머지는 가나다순으로 정렬 people = base_filter.filter( 이름__isnull=False ).exclude( 이름__exact='' ).annotate( sequence_order=Case( When(SEQUENCE__isnull=True, then=Value(1)), default=Value(0), output_field=IntegerField(), ) ).order_by('sequence_order', 'SEQUENCE', '이름') # 검색 로그 기록 if query.strip(): log_search(request, query, people.count()) return render(request, 'B_main/partials/card_list.htm', {'people': people}) def vcard_download(request, name): auth_check = check_authentication(request) if auth_check: return auth_check name = unquote(name) if not name: return HttpResponse("Invalid name", status=400) person = get_object_or_404(Person, 이름=name) vcard_content = f"""BEGIN:VCARD VERSION:3.0 N:{person.이름};;;; FN:{person.이름} ORG:{person.소속} TITLE:{person.직책} TEL;CELL:{person.연락처} ADR:;;{person.주소} END:VCARD """ response = HttpResponse(vcard_content, content_type='text/vcard') response['Content-Disposition'] = f'attachment; filename="{person.이름}.vcf"' return response def logout_view(request): request.session.flush() return redirect('/password/') @login_required def my_profile(request): try: person = Person.objects.get(user=request.user) except Person.DoesNotExist: person = None if request.method == 'POST': form = PersonForm(request.POST, instance=person) if form.is_valid(): person = form.save(commit=False) person.user = request.user person.save() return redirect('main') # or any success page else: form = PersonForm(instance=person) return render(request, 'B_main/profile_form.htm', {'form': form}) from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods @login_required @csrf_exempt @require_http_methods(["POST"]) def withdraw(request): """회원탈퇴 뷰""" try: # 현재 사용자의 Person 정보 가져오기 person = Person.objects.get(user=request.user) # User 연결 해제 person.user = None person.save() # User 객체 삭제 (전화번호 계정 삭제) user_phone = request.user.username request.user.delete() # 로그아웃 logout(request) return JsonResponse({'success': True}) except Person.DoesNotExist: return JsonResponse({'success': False, 'error': 'Person 정보를 찾을 수 없습니다.'}) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}) def session_logout(request): try: del request.session['authenticated'] except KeyError: pass return redirect('/') def signup_view(request): import random from .forms import is_allowed_person from django.contrib.auth import login # 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 = request.session.get('signup_step', 1) name = request.session.get('signup_name') phone = request.session.get('signup_phone') code_sent = bool(request.session.get('signup_code')) verified = request.session.get('signup_verified', False) # 1단계: 이름, 전화번호, 인증번호 if step == 1: if request.method == 'POST': form = Step1PhoneForm(request.POST) action = request.POST.get('action') if action == 'send_code': if form.is_valid(): name = form.cleaned_data['name'] phone = form.cleaned_data['phone'] # 폼 검증에서 이미 허가되지 않은 사용자 체크를 했으므로 여기서는 제거 code = str(random.randint(100000, 999999)) # 실제 SMS 발송 sms_result = send_verification_sms(phone, code) if sms_result['success']: request.session['signup_code'] = code request.session['signup_name'] = name request.session['signup_phone'] = phone request.session['signup_verified'] = False request.session['signup_code_sent_at'] = int(time.time()) # 전화번호 인증 로그 기록 log_phone_verification(request, phone) return render(request, 'B_main/signup.html', { 'step': 1, 'form1': form, 'code_sent': True, 'message': '인증번호가 발송되었습니다.' }) else: pass return render(request, 'B_main/signup.html', { 'step': 1, 'form1': form, 'code_sent': False, 'error': '인증번호 발송에 실패했습니다. 잠시 후 다시 시도해주세요.' }) else: # 폼 에러 메시지 확인 error_message = '입력 정보를 확인해주세요.' if form.errors: # 첫 번째 에러 메시지 사용 for field_errors in form.errors.values(): if field_errors: error_message = field_errors[0] break return render(request, 'B_main/signup.html', { 'step': 1, 'form1': form, 'code_sent': False, 'error': error_message }) elif action == 'verify_code': if form.is_valid(): verification_code = form.cleaned_data['verification_code'] session_code = request.session.get('signup_code') code_sent_at = request.session.get('signup_code_sent_at', 0) current_time = int(time.time()) # 인증번호 만료 시간 체크 (3분) if current_time - code_sent_at > 180: return render(request, 'B_main/signup.html', { 'step': 1, 'form1': form, 'code_sent': False, 'error': '인증번호가 만료되었습니다. 다시 발송해주세요.' }) if verification_code and verification_code == session_code: # 인증 성공 request.session['signup_verified'] = True request.session['signup_step'] = 2 return redirect('signup') else: return render(request, 'B_main/signup.html', { 'step': 1, 'form1': form, 'code_sent': True, 'error': '인증번호가 올바르지 않습니다.' }) else: return render(request, 'B_main/signup.html', {'step': 1, 'form1': form, 'code_sent': code_sent}) else: form = Step1PhoneForm() return render(request, 'B_main/signup.html', {'step': 1, 'form1': form, 'code_sent': False}) # 2단계: 이메일, 비밀번호, 비밀번호 확인 if step == 2: # 세션이 만료되어 인증 정보가 없는 경우 if not verified or not name or not phone: # 세션 초기화 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 form = Step1PhoneForm() return render(request, 'B_main/signup.html', { 'step': 1, 'form1': form, 'code_sent': False, 'error': '세션이 만료되었습니다. 다시 인증해주세요.' }) if request.method == 'POST': form2 = Step2AccountForm(request.POST) if form2.is_valid(): user = form2.save(name, phone, request) # 회원가입 로그 기록 log_signup(request, user) login(request, user, backend='django.contrib.auth.backends.ModelBackend') # 세션 정리 for key in ['signup_code', 'signup_name', 'signup_phone', 'signup_verified', 'signup_step']: request.session.pop(key, None) return redirect('main') else: return render(request, 'B_main/signup.html', {'step': 2, 'form2': form2, 'name': name, 'phone': phone}) else: form2 = Step2AccountForm() return render(request, 'B_main/signup.html', {'step': 2, 'form2': form2, 'name': name, 'phone': phone}) # 기본: 1단계로 초기화 request.session['signup_step'] = 1 request.session['signup_verified'] = False return redirect('signup') def privacy_policy(request): """개인정보처리방침 페이지""" return render(request, 'privacy_policy.html') def test_500_error(request): """500 에러 페이지 테스트용 뷰""" # 강제로 에러를 발생시킵니다 raise Exception("500 에러 페이지 테스트를 위한 의도적인 에러입니다.")