SillaAMP_V2/B_main/views.py

454 lines
17 KiB
Python
Raw Normal View History

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=''
)
# 현재 사용자의 권한에 따라 추가 필터 적용
2025-08-03 12:49:28 +09:00
if request.user.is_superuser or current_user_person is None:
# 모든 사람 표시 (필터 추가 없음)
pass
2025-08-03 12:49:28 +09:00
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
# 강제 리셋 파라미터 확인 (로그인 페이지에서 오는 경우)
force_reset = request.GET.get('reset', '').lower() == 'true'
# GET 요청 시 세션 초기화 (새로운 회원가입 시작)
# 단, 인증번호 확인 후 리다이렉트된 경우는 세션 유지
# 또한 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')
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)
# 텔레그램 알림 전송 (비동기)
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': '인증번호가 발송되었습니다.'
})
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
# 인증 성공 메시지와 함께 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': '인증번호가 올바르지 않습니다.'
})
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 에러 페이지 테스트를 위한 의도적인 에러입니다.")
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')