첫커밋
This commit is contained in:
commit
f9713153d1
12
fastapi/Dockerfile-fastapi
Normal file
12
fastapi/Dockerfile-fastapi
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
FROM python:3.8
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY ./app /app
|
||||||
|
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
EXPOSE 8097
|
||||||
|
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8097"]
|
||||||
|
|
BIN
fastapi/app/__pycache__/main.cpython-38.pyc
Normal file
BIN
fastapi/app/__pycache__/main.cpython-38.pyc
Normal file
Binary file not shown.
11
fastapi/app/common/config.py
Normal file
11
fastapi/app/common/config.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# 서버 종류
|
||||||
|
MY_SERVER = 'SERVER2'
|
||||||
|
|
||||||
|
# JWT 키
|
||||||
|
SECRET_KEY = 'ALLSCORE'
|
||||||
|
|
||||||
|
# 이메일 정보
|
||||||
|
SMTP_USERNAME = 'aofhd0003@gmail.com'
|
||||||
|
SMTP_PASSWORD = 'jueyodpzlvisrtto'
|
||||||
|
SMTP_SERVER = 'smtp.gmail.com'
|
||||||
|
SMTP_PORT = 587
|
BIN
fastapi/app/db/__pycache__/base.cpython-38.pyc
Normal file
BIN
fastapi/app/db/__pycache__/base.cpython-38.pyc
Normal file
Binary file not shown.
BIN
fastapi/app/db/__pycache__/crud.cpython-38.pyc
Normal file
BIN
fastapi/app/db/__pycache__/crud.cpython-38.pyc
Normal file
Binary file not shown.
BIN
fastapi/app/db/__pycache__/models.cpython-38.pyc
Normal file
BIN
fastapi/app/db/__pycache__/models.cpython-38.pyc
Normal file
Binary file not shown.
BIN
fastapi/app/db/__pycache__/schemas.cpython-38.pyc
Normal file
BIN
fastapi/app/db/__pycache__/schemas.cpython-38.pyc
Normal file
Binary file not shown.
18
fastapi/app/db/base.py
Normal file
18
fastapi/app/db/base.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
SQLALCHEMY_DATABASE_URL = "postgresql://eldsoft:eld240510@172.31.7.121:8088/allscore"
|
||||||
|
engine = create_engine(SQLALCHEMY_DATABASE_URL)
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
db = None
|
||||||
|
try:
|
||||||
|
db = SessionLocal()
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
20
fastapi/app/db/crud.py
Normal file
20
fastapi/app/db/crud.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy import text
|
||||||
|
from db import models, schemas
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
def create_card_expense(db: Session, card_expense: schemas.CardExpenseCreate):
|
||||||
|
db_card_expense = models.CardExpense(jang=card_expense.jang, yeo=card_expense.yeo, kim=card_expense.kim, choi=card_expense.choi, amount=card_expense.amount, purpose=card_expense.purpose, created_at=datetime.now())
|
||||||
|
db.add(db_card_expense)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_card_expense)
|
||||||
|
return db_card_expense
|
||||||
|
|
||||||
|
|
||||||
|
def get_card_expenses_by_date(db: Session, year: int, month: int):
|
||||||
|
query = text("""
|
||||||
|
SELECT * FROM card_expenses
|
||||||
|
WHERE EXTRACT(YEAR FROM created_at) = :year
|
||||||
|
AND EXTRACT(MONTH FROM created_at) = :month
|
||||||
|
""")
|
||||||
|
return db.execute(query, {'year': year, 'month': month}).fetchall()
|
313
fastapi/app/db/crud_room_score.py
Normal file
313
fastapi/app/db/crud_room_score.py
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy import text
|
||||||
|
from db import models, schemas
|
||||||
|
from process.logger import logger
|
||||||
|
from db.base import get_db
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# 회원관리 함수
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
|
# 방 생성
|
||||||
|
def create_room(data, db):
|
||||||
|
query = text(f"""
|
||||||
|
insert into room_main(user_seq, cert_type, user_info, cert_code, cert_expired)
|
||||||
|
values({user_seq}, '{cert_type}', '{user_info}', '{cert_code}', now() + INTERVAL '5 minutes')
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 로그인
|
||||||
|
def do_login(user_id, user_pw, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_seq
|
||||||
|
from manage_user
|
||||||
|
where
|
||||||
|
user_id = '{user_id}'
|
||||||
|
and user_pw = (select
|
||||||
|
encode(
|
||||||
|
digest(
|
||||||
|
'{user_pw}' || (select user_pw_solt from manage_user where user_id = '{user_id}'), 'sha256'
|
||||||
|
), 'hex'
|
||||||
|
))
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 마지막 로그인 시간 업데이트
|
||||||
|
def update_last_login_dt(user_seq, db):
|
||||||
|
query = text(f"""
|
||||||
|
update manage_user
|
||||||
|
set last_login_dt = now()
|
||||||
|
where user_seq = {user_seq}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 아이디 찾기(닉네임, 이메일)
|
||||||
|
def find_id_by_name_email(nickname, user_email, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_seq,
|
||||||
|
user_id
|
||||||
|
from manage_user
|
||||||
|
where nickname = '{nickname}'
|
||||||
|
and user_email = '{user_email}'
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 아이디 찾기(user_seq)
|
||||||
|
def find_id_by_user_seq(user_seq, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_id,
|
||||||
|
user_email
|
||||||
|
from manage_user
|
||||||
|
where user_seq = {user_seq}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 이메일 발송 3회 이상 됐는지 확인
|
||||||
|
def select_send_email_cnt(user_email, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
count(user_cert_seq)
|
||||||
|
from manage_user_cert
|
||||||
|
where user_info = '{user_email}'
|
||||||
|
and cert_expired between current_date and current_date + interval '1 day'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 이메일 발송내역 기록(하루 3회만 발송가능하게 하기 위함)
|
||||||
|
def insert_send_email_info(user_seq, cert_type, user_info, cert_code, db):
|
||||||
|
query = text(f"""
|
||||||
|
insert into manage_user_cert(user_seq, cert_type, user_info, cert_code, cert_expired)
|
||||||
|
values({user_seq}, '{cert_type}', '{user_info}', '{cert_code}', now() + INTERVAL '5 minutes')
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 비밀번호 찾기
|
||||||
|
def find_password_by_id_email(user_id, user_email, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_seq
|
||||||
|
from manage_user
|
||||||
|
where user_id = '{user_id}'
|
||||||
|
and user_email = '{user_email}'
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 신규 비밀번호 업데이트
|
||||||
|
def update_new_password(user_seq, new_pw, new_solt, db):
|
||||||
|
query = text(f"""
|
||||||
|
update manage_user
|
||||||
|
set
|
||||||
|
user_pw = encode(digest(encode(digest('{new_pw}', 'sha256'), 'hex') || '{new_solt}', 'sha256'), 'hex'),
|
||||||
|
user_pw_solt = '{new_solt}'
|
||||||
|
where
|
||||||
|
user_seq = {user_seq}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 회원가입 최종 아이디 중복 재검사
|
||||||
|
def is_valid_user_id_by_user_id(user_id, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_seq
|
||||||
|
from manage_user
|
||||||
|
where user_id = '{user_id}'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 신규 유저 등록
|
||||||
|
def insert_new_user(user_info, db):
|
||||||
|
query = text(f"""
|
||||||
|
insert into
|
||||||
|
manage_user(
|
||||||
|
user_id,
|
||||||
|
user_pw,
|
||||||
|
user_pw_solt,
|
||||||
|
nickname,
|
||||||
|
profile_img,
|
||||||
|
user_email,
|
||||||
|
department,
|
||||||
|
introduce_myself,
|
||||||
|
last_login_dt,
|
||||||
|
create_dt,
|
||||||
|
update_dt,
|
||||||
|
mandatory_terms_yn,
|
||||||
|
withdraw_yn
|
||||||
|
)
|
||||||
|
values(
|
||||||
|
'{user_info['user_id']}',
|
||||||
|
(select encode(digest('{user_info['user_pw']}{user_info['user_pw_solt']}', 'sha256'), 'hex')),
|
||||||
|
'{user_info['user_pw_solt']}',
|
||||||
|
'{user_info['nickname']}',
|
||||||
|
'images/user/temp_dir/profile_img/basic_{user_info['user_id']}.png',
|
||||||
|
'{user_info['user_email']}',
|
||||||
|
'{user_info.get('department', '')}',
|
||||||
|
'{user_info.get('introduce_myself', '')}',
|
||||||
|
now(),
|
||||||
|
now(),
|
||||||
|
now(),
|
||||||
|
'{user_info.get('mandatory_terms_yn', '')}',
|
||||||
|
'N'
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# user_seq로 내정보 가져오기
|
||||||
|
def get_my_info_by_user_seq(user_seq, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
nickname,
|
||||||
|
user_email,
|
||||||
|
department,
|
||||||
|
profile_img,
|
||||||
|
introduce_myself
|
||||||
|
from manage_user
|
||||||
|
where user_seq = {user_seq}
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 현재 비밀번호 일치 확인
|
||||||
|
def check_current_user_pw(user_seq, user_pw, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
count(user_seq)
|
||||||
|
from manage_user
|
||||||
|
where user_seq = {user_seq}
|
||||||
|
and user_pw = (select
|
||||||
|
encode(
|
||||||
|
digest(
|
||||||
|
'{user_pw}' || (select user_pw_solt from manage_user where user_seq = '{user_seq}'), 'sha256'
|
||||||
|
), 'hex'
|
||||||
|
))
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 신규 비밀번호 업데이트
|
||||||
|
def update_user_info(user_info, db):
|
||||||
|
if user_info['user_pw_change_yn'] == 'Y':
|
||||||
|
query = text(f"""
|
||||||
|
update manage_user
|
||||||
|
set
|
||||||
|
user_pw = encode(digest(encode(digest('{user_info['new_user_pw']}', 'sha256'), 'hex') || '{user_info['user_pw_solt']}', 'sha256'), 'hex'),
|
||||||
|
user_pw_solt = '{user_info['user_pw_solt']}'
|
||||||
|
nickname = '{user_info['nickname']}',
|
||||||
|
user_email = '{user_info['user_email']}',
|
||||||
|
department = '{user_info['department']}',
|
||||||
|
profile_img = '{user_info['profile_img']}',
|
||||||
|
introduce_myself = '{user_info['introduce_myself']}'
|
||||||
|
where
|
||||||
|
user_seq = {user_info['user_seq']}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
else:
|
||||||
|
query = text(f"""
|
||||||
|
update manage_user
|
||||||
|
set
|
||||||
|
nickname = '{user_info['nickname']}',
|
||||||
|
user_email = '{user_info['user_email']}',
|
||||||
|
department = '{user_info['department']}',
|
||||||
|
profile_img = '{user_info['profile_img']}',
|
||||||
|
introduce_myself = '{user_info['introduce_myself']}'
|
||||||
|
where
|
||||||
|
user_seq = {user_info['user_seq']}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 회원 탈퇴 처리
|
||||||
|
def user_withdraw(user_seq, db):
|
||||||
|
query = text(f"""
|
||||||
|
delete from manage_user
|
||||||
|
where
|
||||||
|
user_seq = {user_seq}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
276
fastapi/app/db/crud_user.py
Normal file
276
fastapi/app/db/crud_user.py
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy import text
|
||||||
|
from db import models, schemas
|
||||||
|
from process.logger import logger
|
||||||
|
from db.base import get_db
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# 회원관리 함수
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
|
# 로그인
|
||||||
|
def do_login(user_id, user_pw, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_seq
|
||||||
|
from manage_user
|
||||||
|
where
|
||||||
|
user_id = '{user_id}'
|
||||||
|
and user_pw = (select
|
||||||
|
encode(
|
||||||
|
digest(
|
||||||
|
'{user_pw}' || (select user_pw_solt from manage_user where user_id = '{user_id}'), 'sha256'
|
||||||
|
), 'hex'
|
||||||
|
))
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 마지막 로그인 시간 업데이트
|
||||||
|
def update_last_login_dt(user_seq, db):
|
||||||
|
query = text(f"""
|
||||||
|
update manage_user
|
||||||
|
set last_login_dt = now()
|
||||||
|
where user_seq = {user_seq}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 아이디 찾기(닉네임, 이메일)
|
||||||
|
def find_id_by_name_email(nickname, user_email, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_seq,
|
||||||
|
user_id
|
||||||
|
from manage_user
|
||||||
|
where nickname = '{nickname}'
|
||||||
|
and user_email = '{user_email}'
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 아이디 찾기(user_seq)
|
||||||
|
def find_id_by_user_seq(user_seq, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_id,
|
||||||
|
user_email
|
||||||
|
from manage_user
|
||||||
|
where user_seq = {user_seq}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 이메일 발송 3회 이상 됐는지 확인
|
||||||
|
def select_send_email_cnt(user_email, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
count(user_cert_seq)
|
||||||
|
from manage_user_cert
|
||||||
|
where user_info = '{user_email}'
|
||||||
|
and cert_expired between current_date and current_date + interval '1 day'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 이메일 발송내역 기록(하루 3회만 발송가능하게 하기 위함)
|
||||||
|
def insert_send_email_info(user_seq, cert_type, user_info, cert_code, db):
|
||||||
|
query = text(f"""
|
||||||
|
insert into manage_user_cert(user_seq, cert_type, user_info, cert_code, cert_expired)
|
||||||
|
values({user_seq}, '{cert_type}', '{user_info}', '{cert_code}', now() + INTERVAL '5 minutes')
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 비밀번호 찾기
|
||||||
|
def find_password_by_id_email(user_id, user_email, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_seq
|
||||||
|
from manage_user
|
||||||
|
where user_id = '{user_id}'
|
||||||
|
and user_email = '{user_email}'
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 신규 비밀번호 업데이트
|
||||||
|
def update_new_password(user_seq, new_pw, new_solt, db):
|
||||||
|
query = text(f"""
|
||||||
|
update manage_user
|
||||||
|
set
|
||||||
|
user_pw = encode(digest(encode(digest('{new_pw}', 'sha256'), 'hex') || '{new_solt}', 'sha256'), 'hex'),
|
||||||
|
user_pw_solt = '{new_solt}'
|
||||||
|
where
|
||||||
|
user_seq = {user_seq}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 회원가입 최종 아이디 중복 재검사
|
||||||
|
def is_valid_user_id_by_user_id(user_id, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
user_seq
|
||||||
|
from manage_user
|
||||||
|
where user_id = '{user_id}'
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 신규 유저 등록
|
||||||
|
def insert_new_user(user_info, db):
|
||||||
|
query = text(f"""
|
||||||
|
insert into
|
||||||
|
manage_user(
|
||||||
|
user_id,
|
||||||
|
user_pw,
|
||||||
|
user_pw_solt,
|
||||||
|
nickname,
|
||||||
|
profile_img,
|
||||||
|
user_email,
|
||||||
|
department,
|
||||||
|
introduce_myself,
|
||||||
|
last_login_dt,
|
||||||
|
create_dt,
|
||||||
|
update_dt,
|
||||||
|
mandatory_terms_yn,
|
||||||
|
withdraw_yn
|
||||||
|
)
|
||||||
|
values(
|
||||||
|
'{user_info['user_id']}',
|
||||||
|
(select encode(digest('{user_info['user_pw']}{user_info['user_pw_solt']}', 'sha256'), 'hex')),
|
||||||
|
'{user_info['user_pw_solt']}',
|
||||||
|
'{user_info['nickname']}',
|
||||||
|
'images/user/temp_dir/profile_img/basic_{user_info['user_id']}.png',
|
||||||
|
'{user_info['user_email']}',
|
||||||
|
'{user_info.get('department', '')}',
|
||||||
|
'{user_info.get('introduce_myself', '')}',
|
||||||
|
now(),
|
||||||
|
now(),
|
||||||
|
now(),
|
||||||
|
'{user_info.get('mandatory_terms_yn', '')}',
|
||||||
|
'N'
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# user_seq로 내정보 가져오기
|
||||||
|
def get_my_info_by_user_seq(user_seq, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
nickname,
|
||||||
|
user_email,
|
||||||
|
department,
|
||||||
|
profile_img,
|
||||||
|
introduce_myself
|
||||||
|
from manage_user
|
||||||
|
where user_seq = {user_seq}
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 현재 비밀번호 일치 확인
|
||||||
|
def check_current_user_pw(user_seq, user_pw, db):
|
||||||
|
query = text(f"""
|
||||||
|
select
|
||||||
|
count(user_seq)
|
||||||
|
from manage_user
|
||||||
|
where user_seq = {user_seq}
|
||||||
|
and user_pw = (select
|
||||||
|
encode(
|
||||||
|
digest(
|
||||||
|
'{user_pw}' || (select user_pw_solt from manage_user where user_seq = '{user_seq}'), 'sha256'
|
||||||
|
), 'hex'
|
||||||
|
))
|
||||||
|
""")
|
||||||
|
return db.execute(query).fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
# 신규 비밀번호 업데이트
|
||||||
|
def update_user_info(user_info, db):
|
||||||
|
if user_info['user_pw_change_yn'] == 'Y':
|
||||||
|
query = text(f"""
|
||||||
|
update manage_user
|
||||||
|
set
|
||||||
|
user_pw = encode(digest(encode(digest('{user_info['new_user_pw']}', 'sha256'), 'hex') || '{user_info['user_pw_solt']}', 'sha256'), 'hex'),
|
||||||
|
user_pw_solt = '{user_info['user_pw_solt']}'
|
||||||
|
nickname = '{user_info['nickname']}',
|
||||||
|
user_email = '{user_info['user_email']}',
|
||||||
|
department = '{user_info['department']}',
|
||||||
|
profile_img = '{user_info['profile_img']}',
|
||||||
|
introduce_myself = '{user_info['introduce_myself']}'
|
||||||
|
where
|
||||||
|
user_seq = {user_info['user_seq']}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
else:
|
||||||
|
query = text(f"""
|
||||||
|
update manage_user
|
||||||
|
set
|
||||||
|
nickname = '{user_info['nickname']}',
|
||||||
|
user_email = '{user_info['user_email']}',
|
||||||
|
department = '{user_info['department']}',
|
||||||
|
profile_img = '{user_info['profile_img']}',
|
||||||
|
introduce_myself = '{user_info['introduce_myself']}'
|
||||||
|
where
|
||||||
|
user_seq = {user_info['user_seq']}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 회원 탈퇴 처리
|
||||||
|
def user_withdraw(user_seq, db):
|
||||||
|
query = text(f"""
|
||||||
|
delete from manage_user
|
||||||
|
where
|
||||||
|
user_seq = {user_seq}
|
||||||
|
and withdraw_yn = 'N'
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
db.execute(query)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"sql error: {e}")
|
||||||
|
return False
|
||||||
|
|
63
fastapi/app/db/models.py
Normal file
63
fastapi/app/db/models.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
from sqlalchemy import Column, Integer, String, CHAR, TIMESTAMP, VARCHAR, FLOAT, JSON, Numeric, DateTime, func, text
|
||||||
|
from .base import Base
|
||||||
|
|
||||||
|
|
||||||
|
class RawDatas(Base):
|
||||||
|
__tablename__ = "col_test_raw_data"
|
||||||
|
|
||||||
|
raw_dt = Column(TIMESTAMP, primary_key=True)
|
||||||
|
mg_equip_rtu_seq = Column(VARCHAR, primary_key=True)
|
||||||
|
acc_pg = Column(VARCHAR)
|
||||||
|
pg = Column(VARCHAR)
|
||||||
|
|
||||||
|
|
||||||
|
class Stat_pwr_rtu_1d_test(Base):
|
||||||
|
__tablename__ = "stat_pwr_rtu_1d_test"
|
||||||
|
|
||||||
|
stat_dt = Column(TIMESTAMP, primary_key=True)
|
||||||
|
rtu_seq = Column(Integer, primary_key=True)
|
||||||
|
stat_value = Column(FLOAT)
|
||||||
|
|
||||||
|
|
||||||
|
class Stat_pwr_rtu_1d(Base):
|
||||||
|
__tablename__ = "stat_pwr_rtu_1d"
|
||||||
|
|
||||||
|
stat_dt = Column(TIMESTAMP, primary_key=True)
|
||||||
|
rtu_seq = Column(Integer, primary_key=True)
|
||||||
|
stat_value = Column(FLOAT)
|
||||||
|
|
||||||
|
|
||||||
|
class Stat_weather_icsr_dc10tca_1d(Base):
|
||||||
|
__tablename__ = "stat_weather_icsr_dc10tca_1d"
|
||||||
|
|
||||||
|
tm = Column(TIMESTAMP, primary_key=True)
|
||||||
|
icsr_station_id = Column(VARCHAR)
|
||||||
|
icsr = Column(VARCHAR)
|
||||||
|
dc10tca_station_id = Column(VARCHAR)
|
||||||
|
dc10tca = Column(VARCHAR)
|
||||||
|
update_dt = Column(TIMESTAMP, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Raw_data_herit(Base):
|
||||||
|
__tablename__ = "raw_data_herit"
|
||||||
|
|
||||||
|
seq = Column(Integer, primary_key=True, autoincrement="auto")
|
||||||
|
tm = Column(TIMESTAMP)
|
||||||
|
content_type = Column(VARCHAR)
|
||||||
|
x_hit_transactionid = Column(VARCHAR)
|
||||||
|
content_length = Column(VARCHAR)
|
||||||
|
body = Column(JSON)
|
||||||
|
|
||||||
|
|
||||||
|
class CardExpense(Base):
|
||||||
|
__tablename__ = "card_expenses"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
jang = Column(String(1), nullable=False)
|
||||||
|
yeo = Column(String(1), nullable=False)
|
||||||
|
kim = Column(String(1), nullable=False)
|
||||||
|
choi = Column(String(1), nullable=False)
|
||||||
|
amount = Column(Numeric(10), nullable=False)
|
||||||
|
purpose = Column(String(255), nullable=False)
|
||||||
|
created_at = Column(DateTime, nullable=False)
|
||||||
|
|
54
fastapi/app/db/schemas.py
Normal file
54
fastapi/app/db/schemas.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class RawData(BaseModel):
|
||||||
|
raw_dt: str
|
||||||
|
mg_equip_rtu_seq: str
|
||||||
|
acc_pg: str
|
||||||
|
pg: str
|
||||||
|
|
||||||
|
|
||||||
|
class RtuGenerator(BaseModel):
|
||||||
|
month: float
|
||||||
|
day: float
|
||||||
|
hour: float
|
||||||
|
rtu_seq: float
|
||||||
|
icsr: float
|
||||||
|
dc10tca: float
|
||||||
|
|
||||||
|
|
||||||
|
class RawDataHerit(BaseModel):
|
||||||
|
tm: str
|
||||||
|
content_type: str
|
||||||
|
X_HIT_TransactionId: str
|
||||||
|
content_Length: str
|
||||||
|
body: dict
|
||||||
|
|
||||||
|
|
||||||
|
class CardExpenseCreate(BaseModel):
|
||||||
|
jang: str
|
||||||
|
yeo: str
|
||||||
|
kim: str
|
||||||
|
choi: str
|
||||||
|
amount: float
|
||||||
|
purpose: str
|
||||||
|
|
||||||
|
|
||||||
|
class CardExpense(BaseModel):
|
||||||
|
id: int
|
||||||
|
jang: str
|
||||||
|
yeo: str
|
||||||
|
kim: str
|
||||||
|
choi: str
|
||||||
|
amount: float
|
||||||
|
purpose: str
|
||||||
|
created_at: datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
|
class DatePayload(BaseModel):
|
||||||
|
year: int
|
||||||
|
month: int
|
0
fastapi/app/error_request.log
Normal file
0
fastapi/app/error_request.log
Normal file
83
fastapi/app/main.py
Normal file
83
fastapi/app/main.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from router.router_api import router as router_api
|
||||||
|
from router.card_service import router as router_card
|
||||||
|
from router.image_api import router as image_api
|
||||||
|
from router.manage_user_api import router as manage_user_api
|
||||||
|
from router.room_score_api import router as room_score_api
|
||||||
|
|
||||||
|
# test
|
||||||
|
from router.test_service import router as test_service_api
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
# docker로 수행하기 때문에 로그는 생략
|
||||||
|
# logger = logging.getLogger()
|
||||||
|
# logger.setLevel(logging.INFO)
|
||||||
|
# file_handler = logging.FileHandler("testtest.log")
|
||||||
|
# logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# CORS 설정
|
||||||
|
origins = [
|
||||||
|
"http://localhost",
|
||||||
|
"http://localhost:3000",
|
||||||
|
"https://localhost",
|
||||||
|
"https://localhost:3000",
|
||||||
|
"http://eldsoft.com",
|
||||||
|
"http://eldsoft.com:3000",
|
||||||
|
"https://eldsoft.com",
|
||||||
|
"https://eldsoft.com:3000"
|
||||||
|
]
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=origins,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# /api
|
||||||
|
app.include_router(router_api)
|
||||||
|
|
||||||
|
# /api/card
|
||||||
|
app.include_router(router_card)
|
||||||
|
|
||||||
|
# /images
|
||||||
|
app.include_router(image_api)
|
||||||
|
|
||||||
|
# /user
|
||||||
|
app.include_router(manage_user_api)
|
||||||
|
|
||||||
|
# /room/score
|
||||||
|
app.include_router(room_score_api)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# /test
|
||||||
|
app.include_router(test_service_api)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# /iot-kt
|
||||||
|
# app.include_router(router_iot_kt)
|
||||||
|
# /iot-lg
|
||||||
|
# app.include_router(router_iot_lg)
|
||||||
|
|
||||||
|
@app.get("/ttt")
|
||||||
|
async def test_endpoint(data: dict):
|
||||||
|
return {"get received_data": data}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/ttt")
|
||||||
|
async def test_endpoint(data: dict):
|
||||||
|
return {"received_data": data}
|
9
fastapi/app/middleware/middleware.py
Normal file
9
fastapi/app/middleware/middleware.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from fastapi import FastAPI, Request
|
||||||
|
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
||||||
|
from starlette.responses import Response
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class TimeHeaderMiddleware(BaseHTTPMiddleware) :
|
||||||
|
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
||||||
|
return await super().dispatch(request, call_next)
|
167
fastapi/app/parse.py
Normal file
167
fastapi/app/parse.py
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
{
|
||||||
|
'device_id': 'HERIT-HC1000N4-359689092518957',
|
||||||
|
'parent_id': 'herit-gw_nbiot',
|
||||||
|
'sid': '1',
|
||||||
|
'msg': {
|
||||||
|
'o': 'n',
|
||||||
|
'e': [
|
||||||
|
{
|
||||||
|
'sv': '211',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/99/0/5'
|
||||||
|
}, {
|
||||||
|
'sv': '55,57,51',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/99/0/6'
|
||||||
|
}, {
|
||||||
|
'sv': '24,22,1,1',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/99/0/8'
|
||||||
|
}, {
|
||||||
|
'sv': '24,24,0,0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/99/0/9'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/99/0/10'
|
||||||
|
}, {
|
||||||
|
'sv': '9778500',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/0'
|
||||||
|
}, {
|
||||||
|
'sv': '551421',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/1'
|
||||||
|
}, {
|
||||||
|
'sv': '7700',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/2'
|
||||||
|
}, {
|
||||||
|
'sv': '361',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/3'
|
||||||
|
}, {
|
||||||
|
'sv': '2413',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/4'
|
||||||
|
}, {
|
||||||
|
'sv': '700',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/5'
|
||||||
|
}, {
|
||||||
|
'sv': '291',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/6'
|
||||||
|
}, {
|
||||||
|
'sv': '9.0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/7'
|
||||||
|
}, {
|
||||||
|
'sv': '2619.0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/8'
|
||||||
|
}, {
|
||||||
|
'sv': '235',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/9'
|
||||||
|
}, {
|
||||||
|
'sv': '10.0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/10'
|
||||||
|
}, {
|
||||||
|
'sv': '2413.0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/11'
|
||||||
|
}, {
|
||||||
|
'sv': '98.1',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/12'
|
||||||
|
}, {
|
||||||
|
'sv': '60.0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/100/1/13'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/0'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/102/1/0'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/19'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/20'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/5'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/6'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/11'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/9'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/10'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/3'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/8'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/16'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/14'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/15'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/21'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/17'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/18'
|
||||||
|
}, {
|
||||||
|
'sv': '0',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/101/1/22'
|
||||||
|
}, {
|
||||||
|
'sv': '1',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/200/1/4'
|
||||||
|
}, {
|
||||||
|
'sv': '1',
|
||||||
|
'ti': '1686883200000',
|
||||||
|
'n': '/200/1/5'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
161
fastapi/app/process/certification/cert_process.py
Normal file
161
fastapi/app/process/certification/cert_process.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
from fastapi import Request
|
||||||
|
import json
|
||||||
|
from process.logger import logger
|
||||||
|
import jwt
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
from common.config import SECRET_KEY
|
||||||
|
|
||||||
|
async def cert_process_by_token(request: Request):
|
||||||
|
# 헤더에서 토큰 찾기
|
||||||
|
auth_token = request.headers.get('auth-token')
|
||||||
|
# 헤더에 토큰이 없으면 인증 거절 처리
|
||||||
|
if auth_token == None:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
"token": ""
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# 헤더에 토큰이 있으므로 DB에서 유효한 토큰 값인지 검증하기
|
||||||
|
1
|
||||||
|
|
||||||
|
|
||||||
|
def create_jwt(user_seq: int, period: int):
|
||||||
|
# 현재 시간
|
||||||
|
now = int(time.time())
|
||||||
|
|
||||||
|
# JWT의 페이로드 (클레임)
|
||||||
|
payload = {
|
||||||
|
"user_seq": user_seq,
|
||||||
|
"exp": now + period, # 만료 시간
|
||||||
|
"iat": now, # 토큰 발행 시간
|
||||||
|
}
|
||||||
|
|
||||||
|
# JWT 생성 (HS256 알고리즘 사용)
|
||||||
|
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
|
||||||
|
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
|
def decode_jwt(token: str):
|
||||||
|
try:
|
||||||
|
# 토큰 디코드
|
||||||
|
decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"data": decoded_payload
|
||||||
|
}
|
||||||
|
except jwt.ExpiredSignatureError:
|
||||||
|
return {
|
||||||
|
"result": "TOKEN_EXPIRED",
|
||||||
|
"error": "Token has expired"
|
||||||
|
}
|
||||||
|
except jwt.InvalidTokenError:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
"error": "Invalid token"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 인증정보 JWT토큰화
|
||||||
|
def create_user_cert_seq_jwt(user_cert_seq: int, period: int):
|
||||||
|
# 현재 시간
|
||||||
|
now = int(time.time())
|
||||||
|
|
||||||
|
# JWT의 페이로드 (클레임)
|
||||||
|
payload = {
|
||||||
|
"user_cert_seq": user_cert_seq,
|
||||||
|
"exp": now + period, # 만료 시간
|
||||||
|
"iat": now, # 토큰 발행 시간
|
||||||
|
}
|
||||||
|
|
||||||
|
# JWT 생성 (HS256 알고리즘 사용)
|
||||||
|
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
|
||||||
|
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
|
# 인증서 갱신
|
||||||
|
def renew_cert(request: Request):
|
||||||
|
try:
|
||||||
|
# 헤더에서 토큰 찾기
|
||||||
|
auth_token = request.headers.get('auth-token')
|
||||||
|
decoded_payload = decode_jwt(auth_token)
|
||||||
|
if decoded_payload['result'] == 'OK':
|
||||||
|
decoded_payload = decoded_payload['data']
|
||||||
|
user_seq = decoded_payload['user_seq']
|
||||||
|
exp = decoded_payload['exp']
|
||||||
|
iat = decoded_payload['iat']
|
||||||
|
now = int(time.time())
|
||||||
|
# 시간비교 유효한 토큰인지 확인
|
||||||
|
if iat <= now and now <= exp:
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"data": make_auth_data(create_jwt(user_seq=user_seq, period=3600), 'Y')
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "TOKEN_EXPIRED",
|
||||||
|
"data": make_auth_data('', 'N'),
|
||||||
|
"msg": '인증서 토큰이 만료 되었습니다.'
|
||||||
|
}
|
||||||
|
elif decoded_payload['result'] == 'TOKEN_EXPIRED':
|
||||||
|
return {
|
||||||
|
"result": "TOKEN_EXPIRED",
|
||||||
|
"data": make_auth_data('', 'N'),
|
||||||
|
"msg": '인증서 토큰이 만료 되었습니다.'
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
logger.error(f"token renew error. message: {e}")
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
"data": make_auth_data('', 'N'),
|
||||||
|
"msg": decoded_payload['error']
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"token renew error. message: {e}")
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
"data": make_auth_data('', 'N'),
|
||||||
|
"msg": '인증서 토큰 갱신에 실패했습니다.'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 응답 auth 함수화
|
||||||
|
def make_auth_data(token, tf):
|
||||||
|
return {
|
||||||
|
"renew_yn": tf,
|
||||||
|
"token": token
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 토큰으로 user_seq 가져오기
|
||||||
|
def get_user_seq_by_token(token: str):
|
||||||
|
try:
|
||||||
|
payload = decode_jwt(token)
|
||||||
|
if payload['result'] == 'OK':
|
||||||
|
user_seq = payload['data']['user_seq']
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"data":{
|
||||||
|
"user_seq": user_seq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
"msg": payload['error']
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"token error. message: {e}")
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
"msg": 'get_user_seq_by_token error'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
106
fastapi/app/process/email/email_user.py
Normal file
106
fastapi/app/process/email/email_user.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
from fastapi import FastAPI, HTTPException, BackgroundTasks
|
||||||
|
from pydantic import BaseModel, EmailStr
|
||||||
|
import smtplib
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
|
from process.logger import logger
|
||||||
|
from common.config import SMTP_PASSWORD, SMTP_PORT, SMTP_SERVER, SMTP_USERNAME
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# HTML 템플릿을 로드하고 렌더링
|
||||||
|
def render_template(template_path, context):
|
||||||
|
with open(template_path, 'r') as file:
|
||||||
|
template = Template(file.read())
|
||||||
|
return template.render(context)
|
||||||
|
|
||||||
|
|
||||||
|
# 메일 발송 함수
|
||||||
|
def send_email(email_to: str, subject: str, html_content: str):
|
||||||
|
try:
|
||||||
|
msg = MIMEMultipart("alternative")
|
||||||
|
msg['From'] = SMTP_USERNAME
|
||||||
|
msg['To'] = email_to
|
||||||
|
msg['Subject'] = subject
|
||||||
|
|
||||||
|
msg.attach(MIMEText(html_content, 'html'))
|
||||||
|
|
||||||
|
server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
|
||||||
|
server.starttls()
|
||||||
|
server.login(SMTP_USERNAME, SMTP_PASSWORD)
|
||||||
|
|
||||||
|
# 이메일 발송
|
||||||
|
server.sendmail(SMTP_USERNAME, email_to, msg.as_string())
|
||||||
|
server.quit()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"result": 'OK',
|
||||||
|
"msg": "이메일 발송에 성공했습니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"이메일 발송에 실패했습니다. error_msg: {e}")
|
||||||
|
return {
|
||||||
|
"result": 'FAIL',
|
||||||
|
"msg": "이메일 발송에 실패했습니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 이메일 발송 API 엔드포인트
|
||||||
|
def send_email_api(context):
|
||||||
|
try:
|
||||||
|
html_content = render_template(f"/app/process/email/{context['template_name']}", context)
|
||||||
|
|
||||||
|
send_email_result = send_email(email_to=context['user_email'], subject=context['email_title'], html_content=html_content)
|
||||||
|
return send_email_result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"이메일 발송에 실패했습니다. error_msg: {e}")
|
||||||
|
return {
|
||||||
|
"result": 'FAIL',
|
||||||
|
"msg": "이메일 발송에 실패했습니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 아이디 찾기 이메일 발송 함수
|
||||||
|
def email_find_id(user_email, info):
|
||||||
|
context = {
|
||||||
|
"email_title": "[ALLSCORE] 아이디가 발송되었습니다.",
|
||||||
|
"info": info,
|
||||||
|
"user_email": user_email,
|
||||||
|
"template_name": "find_id_template.html",
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
return send_email_api(context)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"이메일 발송에 실패했습니다. error_msg: {e}")
|
||||||
|
return {
|
||||||
|
"result": 'FAIL',
|
||||||
|
"msg": "이메일 발송에 실패했습니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 비밀번호 찾기 이메일 발송 함수
|
||||||
|
def email_find_password(user_email, info):
|
||||||
|
context = {
|
||||||
|
"email_title": "[유어라운드] 임시 비밀번호가 발급되었습니다.",
|
||||||
|
"info": info,
|
||||||
|
"user_email": user_email,
|
||||||
|
"template_name": "find_password_template.html",
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
return send_email_api(context)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"이메일 발송에 실패했습니다. error_msg: {e}")
|
||||||
|
return {
|
||||||
|
"result": 'FAIL',
|
||||||
|
"msg": "이메일 발송에 실패했습니다."
|
||||||
|
}
|
24
fastapi/app/process/email/find_id_template.html
Normal file
24
fastapi/app/process/email/find_id_template.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Email Template</title>
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0; padding: 0; font-family: Arial, sans-serif; background-color: #ffffff; text-align: center;">
|
||||||
|
<table role="presentation" style="width: 100%; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #ffffff; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);">
|
||||||
|
<tr>
|
||||||
|
<td style="text-align: center; padding: 20px;">
|
||||||
|
<h1 style="font-size: 24px; color: #333333; margin-bottom: 10px;">고객님의 아이디는 다음과 같습니다.</h1>
|
||||||
|
<p style="font-size: 20px; font-weight: bold; color: #666666; margin-bottom: 20px;">{{ info }}</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="text-align: center; padding: 20px;">
|
||||||
|
<hr style="border-top: 1px solid #d9d9d9; margin: 20px 0;">
|
||||||
|
<p style="font-size: 12px; color: #999999; margin: 5px 0;">본 메일은 발신 전용으로 회신되지 않습니다.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
25
fastapi/app/process/email/find_password_template.html
Normal file
25
fastapi/app/process/email/find_password_template.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Email Template</title>
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0; padding: 0; font-family: Arial, sans-serif; background-color: #ffffff; text-align: center;">
|
||||||
|
<table role="presentation" style="width: 100%; max-width: 700px; margin: 0 auto; padding: 20px; background-color: #ffffff; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);">
|
||||||
|
<tr>
|
||||||
|
<td style="text-align: center; padding: 20px;">
|
||||||
|
<h1 style="font-size: 24px; color: #333333; margin-bottom: 10px;">고객님의 임시 비밀번호는 다음과 같습니다.</h1>
|
||||||
|
<p style="font-size: 20px; font-weight: bold; color: #666666; margin-bottom: 20px;">{{ info }}</p>
|
||||||
|
<h3 style="font-size: 12px; color: #333333; margin-bottom: 10px;">임시 비밀번호로 로그인 한 후 마이페이지에서 비밀번호를 변경해 주세요.</h3>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="text-align: center; padding: 20px;">
|
||||||
|
<hr style="border-top: 1px solid #d9d9d9; margin: 20px 0;">
|
||||||
|
<p style="font-size: 12px; color: #999999; margin: 5px 0;">본 메일은 발신 전용으로 회신되지 않습니다.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
55
fastapi/app/process/logger.py
Normal file
55
fastapi/app/process/logger.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class CustomTimedRotatingFileHandler(TimedRotatingFileHandler):
|
||||||
|
def __init__(self, base_log_path, when="midnight", interval=1, backupCount=7):
|
||||||
|
self.base_log_path = base_log_path
|
||||||
|
# 초기화 시점에 파일 경로를 명시적으로 설정
|
||||||
|
self.update_log_path()
|
||||||
|
super().__init__(self.baseFilename, when=when, interval=interval, backupCount=backupCount)
|
||||||
|
|
||||||
|
def update_log_path(self):
|
||||||
|
current_time = datetime.now()
|
||||||
|
log_directory = os.path.join(
|
||||||
|
self.base_log_path,
|
||||||
|
current_time.strftime("%Y"),
|
||||||
|
current_time.strftime("%m")
|
||||||
|
)
|
||||||
|
log_filename = current_time.strftime("%d_allscore.log")
|
||||||
|
|
||||||
|
# 로그 파일 경로 설정
|
||||||
|
self.baseFilename = os.path.join(log_directory, log_filename)
|
||||||
|
|
||||||
|
# 디렉토리가 없으면 생성
|
||||||
|
if not os.path.exists(log_directory):
|
||||||
|
os.makedirs(log_directory)
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
# 각 로그 기록 시점에서 경로를 다시 확인해 업데이트
|
||||||
|
self.update_log_path()
|
||||||
|
super().emit(record)
|
||||||
|
|
||||||
|
# 로거 설정
|
||||||
|
logger = logging.getLogger("allscore_logger")
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# 핸들러 설정 (stdout으로 출력)
|
||||||
|
stream_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
stream_handler.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# 로그 포맷 설정
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
stream_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# 커스텀 파일 핸들러 설정
|
||||||
|
base_log_path = "/app/logs"
|
||||||
|
file_handler = CustomTimedRotatingFileHandler(base_log_path=base_log_path, when="midnight", interval=1, backupCount=7)
|
||||||
|
file_handler.setLevel(logging.DEBUG)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# 핸들러를 로거에 추가
|
||||||
|
logger.addHandler(stream_handler)
|
||||||
|
logger.addHandler(file_handler)
|
96
fastapi/app/process/response/response.py
Normal file
96
fastapi/app/process/response/response.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
from fastapi import Request
|
||||||
|
import json
|
||||||
|
from process.logger import logger
|
||||||
|
import jwt
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
from process.certification import cert_process
|
||||||
|
from process.user import manage_user
|
||||||
|
|
||||||
|
|
||||||
|
# 성공 응답
|
||||||
|
async def ok_res(auth_token, data, db):
|
||||||
|
try:
|
||||||
|
# 토큰에서 user_seq 찾기
|
||||||
|
user_seq_result = cert_process.get_user_seq_by_token(token=auth_token['token'])
|
||||||
|
if user_seq_result['result'] == 'OK':
|
||||||
|
user_seq = user_seq_result['data']['user_seq']
|
||||||
|
else:
|
||||||
|
return make_response('OK', auth_token['renew_yn'], 'NOMAL', auth_token['token'], '성공', '성공', data)
|
||||||
|
|
||||||
|
return make_response('OK', auth_token['renew_yn'], '', auth_token['token'], '성공', '성공', data)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"ok response error: {e}")
|
||||||
|
return make_response('ERROR', 'N', 'NOMAL', '', '응답 에러', '서버 장애가 발생했습니다.', data)
|
||||||
|
|
||||||
|
|
||||||
|
# 실패 응답
|
||||||
|
async def fail_res(auth_token, auth_type, msg_title, msg_content, data):
|
||||||
|
try:
|
||||||
|
return make_response('FAIL', auth_token['renew_yn'], auth_type, auth_token['token'], msg_title, msg_content, data)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"ok response error: {e}")
|
||||||
|
return make_response('ERROR', 'N', 'NOMAL', '', '응답 에러', '서버 장애가 발생했습니다.', data)
|
||||||
|
|
||||||
|
|
||||||
|
# 에러 응답
|
||||||
|
async def error_res(auth_token, auth_type, msg_title, msg_content, data):
|
||||||
|
try:
|
||||||
|
return make_response('ERROR', auth_token['renew_yn'], auth_type, auth_token['token'], msg_title, msg_content, data)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"ok response error: {e}")
|
||||||
|
return make_response('ERROR', 'N', 'NOMAL', '', '응답 에러', '서버 장애가 발생했습니다.', data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 응답 패턴
|
||||||
|
def make_response(result, renew_yn, auth_type, token, msg_title, msg_content, data):
|
||||||
|
return {
|
||||||
|
"result": result, # OK, FAIL, ERROR
|
||||||
|
"auth": {
|
||||||
|
"renew_yn": renew_yn,
|
||||||
|
"type": auth_type,
|
||||||
|
"token": token,
|
||||||
|
},
|
||||||
|
"response_info": {
|
||||||
|
"msg_type": result, # OK, FAIL, ERROR
|
||||||
|
"msg_title": msg_title,
|
||||||
|
"msg_content": msg_content,
|
||||||
|
},
|
||||||
|
"data": data,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# # 기존 응답 방식
|
||||||
|
# {
|
||||||
|
# "result": "FAIL",
|
||||||
|
# "msg": fail_msg,
|
||||||
|
# "auth": {
|
||||||
|
# "auth-token": {
|
||||||
|
# "renew_yn": 'Y',
|
||||||
|
# "token": '토큰값'
|
||||||
|
# }
|
||||||
|
# },
|
||||||
|
# "data": {}
|
||||||
|
# }
|
||||||
|
|
||||||
|
# # 새로운 응답 방식
|
||||||
|
# {
|
||||||
|
# "result": "FAIL", # OK, FAIL, ERROR
|
||||||
|
# "data": {
|
||||||
|
# "auth": {
|
||||||
|
# "renew_yn": 'Y',
|
||||||
|
# "type": 'ADMIN',
|
||||||
|
# "token": '토큰값'
|
||||||
|
# },
|
||||||
|
# "response_info": {
|
||||||
|
# "msg_type": 'FAIL', # OK, FAIL, ERROR
|
||||||
|
# "msg_title": '제목',
|
||||||
|
# "msg_content": '내용',
|
||||||
|
# },
|
||||||
|
# "data": '데이터',
|
||||||
|
# }
|
||||||
|
# }
|
31
fastapi/app/process/room/room_score.py
Normal file
31
fastapi/app/process/room/room_score.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, Header, Body, status, Request
|
||||||
|
from db import crud_room_score
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from db.base import get_db
|
||||||
|
from db import models, schemas
|
||||||
|
from process.logger import logger
|
||||||
|
|
||||||
|
# 방 만들기
|
||||||
|
async def create_room(data, db):
|
||||||
|
db_result_ori = crud_room_score.create_room(data=data, db=db)
|
||||||
|
db_result = []
|
||||||
|
for db_data in db_result_ori:
|
||||||
|
db_result.append(db_data)
|
||||||
|
|
||||||
|
# 일치하는 유저가 있는지 확인
|
||||||
|
if len(db_result) == 0:
|
||||||
|
return {
|
||||||
|
"result": "FAIL"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"user_seq": db_result[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
240
fastapi/app/process/user/manage_user.py
Normal file
240
fastapi/app/process/user/manage_user.py
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, Header, Body, status, Request
|
||||||
|
from db import crud_user
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from db.base import get_db
|
||||||
|
from db import models, schemas
|
||||||
|
from process.logger import logger
|
||||||
|
|
||||||
|
# 로그인 수행
|
||||||
|
async def do_login(user_id, user_pw, db):
|
||||||
|
# DB에서 유저 정보 확인
|
||||||
|
db_result_ori = crud_user.do_login(user_id=user_id, user_pw=user_pw, db=db)
|
||||||
|
db_result = []
|
||||||
|
for db_data in db_result_ori:
|
||||||
|
db_result.append(db_data)
|
||||||
|
|
||||||
|
# 일치하는 유저가 있는지 확인
|
||||||
|
if len(db_result) == 0:
|
||||||
|
return {
|
||||||
|
"result": "FAIL"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"user_seq": db_result[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 마지막 로그인 시간 업데이트
|
||||||
|
async def update_last_login_dt(user_seq, db):
|
||||||
|
# 마지막 로그인 시간 업데이트
|
||||||
|
db_result = crud_user.update_last_login_dt(user_seq=user_seq, db=db)
|
||||||
|
|
||||||
|
if db_result:
|
||||||
|
return {
|
||||||
|
"result": "OK"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 아이디 찾기(닉네임, 이메일)
|
||||||
|
async def find_id_by_name_email(nickname, user_email, db):
|
||||||
|
# DB에서 회원정보 찾기
|
||||||
|
db_result_ori = crud_user.find_id_by_name_email(nickname=nickname, user_email=user_email, db=db)
|
||||||
|
db_result = []
|
||||||
|
for db_data in db_result_ori:
|
||||||
|
db_result.append(db_data)
|
||||||
|
|
||||||
|
# 일치하는 유저가 있는지 확인
|
||||||
|
if len(db_result) == 0:
|
||||||
|
return {
|
||||||
|
"result": "FAIL"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
db_result = db_result[0]
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"user_seq": db_result[0],
|
||||||
|
"user_id": db_result[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 아이디 찾기(user_seq)
|
||||||
|
async def find_id_by_user_seq(user_seq, db):
|
||||||
|
# DB에서 회원정보 찾기
|
||||||
|
db_result_ori = crud_user.find_id_by_user_seq(user_seq=user_seq, db=db)
|
||||||
|
db_result = []
|
||||||
|
for db_data in db_result_ori:
|
||||||
|
db_result.append(db_data)
|
||||||
|
|
||||||
|
# 일치하는 유저가 있는지 확인
|
||||||
|
if len(db_result) == 0:
|
||||||
|
return {
|
||||||
|
"result": "FAIL"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
db_result = db_result[0]
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"user_id": db_result[0],
|
||||||
|
"user_email": db_result[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 이메일 발송 3회 이상 됐는지 확인
|
||||||
|
async def select_send_email_cnt(user_email, db):
|
||||||
|
db_result_ori = crud_user.select_send_email_cnt(user_email=user_email, db=db)
|
||||||
|
db_result = []
|
||||||
|
for db_data in db_result_ori:
|
||||||
|
db_result.append(db_data)
|
||||||
|
|
||||||
|
db_result = db_result[0][0]
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"send_email_cnt": db_result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 이메일 발송내역 기록(하루 3회만 발송가능하게 하기 위함)
|
||||||
|
async def insert_send_email_info(user_seq, cert_type, user_info, cert_code, db):
|
||||||
|
db_result = crud_user.insert_send_email_info(user_seq=user_seq, cert_type=cert_type, user_info=user_info, cert_code=cert_code, db=db)
|
||||||
|
|
||||||
|
if db_result:
|
||||||
|
return {
|
||||||
|
"result": "OK"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 비밀번호 찾기 수행
|
||||||
|
async def find_password_by_id_email(user_id, user_email, db):
|
||||||
|
# DB에서 회원정보 찾기
|
||||||
|
db_result_ori = crud_user.find_password_by_id_email(user_id=user_id, user_email=user_email, db=db)
|
||||||
|
db_result = []
|
||||||
|
for db_data in db_result_ori:
|
||||||
|
db_result.append(db_data)
|
||||||
|
|
||||||
|
# 일치하는 유저가 있는지 확인
|
||||||
|
if len(db_result) == 0:
|
||||||
|
return {
|
||||||
|
"result": "FAIL"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"user_seq": db_result[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 비밀번호 업데이트 수행
|
||||||
|
async def update_new_password(user_seq, new_pw, new_solt, db):
|
||||||
|
# 회원정보 업데이트
|
||||||
|
db_result = crud_user.update_new_password(user_seq=user_seq, new_pw=new_pw, new_solt=new_solt, db=db)
|
||||||
|
|
||||||
|
if db_result:
|
||||||
|
return {
|
||||||
|
"result": "OK"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 유저 회원가입 진행
|
||||||
|
async def insert_new_user(user_info, db):
|
||||||
|
# DB에서 신규 유저 등록
|
||||||
|
db_result_manage_user = crud_user.insert_new_user(user_info=user_info, db=db)
|
||||||
|
|
||||||
|
# 인증코드 입력 결과
|
||||||
|
if not db_result_manage_user:
|
||||||
|
return {
|
||||||
|
"result": "FAIL"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 마이페이지 - 유저 정보 가져오기
|
||||||
|
async def get_my_info_by_user_seq(user_seq, db):
|
||||||
|
# DB에서 유저 정보 가져오기
|
||||||
|
db_result_ori = crud_user.get_my_info_by_user_seq(user_seq=user_seq, db=db)
|
||||||
|
db_result = []
|
||||||
|
for db_data in db_result_ori:
|
||||||
|
db_result.append(db_data)
|
||||||
|
|
||||||
|
# 데이터 1개만 존재하는지 확인
|
||||||
|
if len(db_result) == 1:
|
||||||
|
# 데이터 key value 구분해주기
|
||||||
|
db_result = db_result[0]
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"data": {
|
||||||
|
"nickname": db_result[0],
|
||||||
|
"user_email": db_result[1],
|
||||||
|
"department": db_result[2],
|
||||||
|
"profile_img": db_result[3],
|
||||||
|
"introduce_myself": db_result[4]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"result": "FAIL"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 현재 비밀번호 일치 확인
|
||||||
|
async def check_current_user_pw(user_seq, user_pw, db):
|
||||||
|
db_result_ori = crud_user.check_current_user_pw(user_seq=user_seq, user_pw=user_pw, db=db)
|
||||||
|
db_result = []
|
||||||
|
for db_data in db_result_ori:
|
||||||
|
db_result.append(db_data)
|
||||||
|
|
||||||
|
if db_result[0][0] == 1:
|
||||||
|
return {
|
||||||
|
"result": "OK"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 회원정보 업데이트
|
||||||
|
async def update_user_info(user_info, db):
|
||||||
|
# 회원정보 업데이트
|
||||||
|
db_result = crud_user.update_user_info(user_info=user_info, db=db)
|
||||||
|
|
||||||
|
if db_result:
|
||||||
|
return {
|
||||||
|
"result": "OK"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 회원 탈퇴 처리
|
||||||
|
async def user_withdraw(user_seq, db):
|
||||||
|
db_result = crud_user.user_withdraw(user_seq=user_seq, db=db)
|
||||||
|
|
||||||
|
if db_result:
|
||||||
|
return {
|
||||||
|
"result": "OK"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
116
fastapi/app/process/user/manage_user_pattern.py
Normal file
116
fastapi/app/process/user/manage_user_pattern.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, Header, Body, status, Request
|
||||||
|
from db import crud_user
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from db import models, schemas
|
||||||
|
from process.logger import logger
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
# 회원가입 입력항목 확인
|
||||||
|
def check_mandatory_insert_list(user_info):
|
||||||
|
# user_id 확인
|
||||||
|
if not validate_user_id(user_id=user_info['user_id']):
|
||||||
|
return '아이디 규칙이 맞지 않습니다.'
|
||||||
|
|
||||||
|
# user_pw 확인
|
||||||
|
if not validate_user_pw(user_pw=user_info['user_pw']):
|
||||||
|
return '비밀번호 암호화가 되지 않았습니다.'
|
||||||
|
|
||||||
|
# nickname 확인
|
||||||
|
if not validate_nickname(nickname=user_info['nickname']):
|
||||||
|
return '사용자 닉네임 규칙이 맞지 않습니다.'
|
||||||
|
|
||||||
|
# user_email 확인
|
||||||
|
if not validate_user_email(user_email=user_info['user_email']):
|
||||||
|
return '이메일 규칙이 맞지 않습니다.'
|
||||||
|
|
||||||
|
# introduce_myself 확인
|
||||||
|
if len(user_info['introduce_myself']) > 2000:
|
||||||
|
return '자기소개 글은 2000byte까지만 입력 가능합니다.'
|
||||||
|
|
||||||
|
# mandatory_terms 확인
|
||||||
|
if user_info['mandatory_terms_yn'] != 'Y':
|
||||||
|
return '필수약관항목에 대해 동의 하지 않았습니다.'
|
||||||
|
|
||||||
|
# 이상없음
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# 회원정보 수정 입력값 검증
|
||||||
|
def check_new_data(user_info):
|
||||||
|
# user_pw 확인
|
||||||
|
if user_info['user_pw_change_yn'] == 'Y':
|
||||||
|
if not validate_user_pw(user_pw=user_info['user_pw']):
|
||||||
|
return '비밀번호 암호화가 되지 않았습니다.'
|
||||||
|
|
||||||
|
# nickname 확인
|
||||||
|
if not validate_nickname(nickname=user_info['nickname']):
|
||||||
|
return '사용자 닉네임 규칙이 맞지 않습니다.'
|
||||||
|
|
||||||
|
# user_email 확인
|
||||||
|
if not validate_user_email(user_email=user_info['user_email']):
|
||||||
|
return '이메일 규칙이 맞지 않습니다.'
|
||||||
|
|
||||||
|
# profile_img 확인
|
||||||
|
if not starts_with_user_dir(user_info['profile_img']):
|
||||||
|
return '프로필 이미지 경로가 정확하지 않습니다.'
|
||||||
|
|
||||||
|
# introduce_myself 확인
|
||||||
|
if len(user_info['introduce_myself']) > 2000:
|
||||||
|
return '자기소개 글은 2000byte까지만 입력 가능합니다.'
|
||||||
|
|
||||||
|
# 이상없음
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# user_id 정규식 검증
|
||||||
|
def validate_user_id(user_id: str) -> bool:
|
||||||
|
pattern = r'^[a-zA-Z0-9]{6,20}$'
|
||||||
|
return bool(re.match(pattern, user_id))
|
||||||
|
|
||||||
|
|
||||||
|
# user_pw sha256 검증
|
||||||
|
def validate_user_pw(user_pw: str) -> bool:
|
||||||
|
return True if len(user_pw) == 64 else False
|
||||||
|
|
||||||
|
|
||||||
|
# nickname 정규식 검증
|
||||||
|
def validate_nickname(nickname: str) -> bool:
|
||||||
|
# 바이트 계산
|
||||||
|
byte_length = sum(2 if ord(char) > 127 else 1 for char in nickname)
|
||||||
|
|
||||||
|
# 정규식으로 한글, 영어, 숫자만 허용
|
||||||
|
pattern = re.compile(r'^[가-힣a-zA-Z0-9]+$')
|
||||||
|
|
||||||
|
if pattern.match(nickname) and 2 <= byte_length <= 20:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# user_email 정규식 검증
|
||||||
|
def validate_user_email(user_email: str) -> bool:
|
||||||
|
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||||
|
return bool(re.match(pattern, user_email))
|
||||||
|
|
||||||
|
|
||||||
|
# user_phone 정규식 검증
|
||||||
|
def validate_user_phone(user_phone: str) -> bool:
|
||||||
|
pattern = r'^010\d{8}$'
|
||||||
|
return bool(re.match(pattern, user_phone))
|
||||||
|
|
||||||
|
|
||||||
|
# 유저 아이디 중복 검사
|
||||||
|
async def is_valid_user_id_by_user_id(user_id, db):
|
||||||
|
# DB에서 user_id 중복 확인하기
|
||||||
|
db_result_ori = crud_user.is_valid_user_id_by_user_id(user_id=user_id, db=db)
|
||||||
|
db_result = []
|
||||||
|
for db_data in db_result_ori:
|
||||||
|
db_result.append(db_data)
|
||||||
|
|
||||||
|
return True if len(db_result) == 0 else False
|
||||||
|
|
||||||
|
|
||||||
|
# profile_img 파일명이 /user/temp_dir/profile_img/IMG 로 시작하는지 검증
|
||||||
|
def starts_with_user_dir(value: str) -> bool:
|
||||||
|
prefix = '/user/temp_dir/profile_img/IMG'
|
||||||
|
return value.startswith(prefix)
|
17
fastapi/app/requirements.txt
Normal file
17
fastapi/app/requirements.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
fastapi>=0.68.0,<0.69.0
|
||||||
|
pydantic>=1.8.0,<2.0.0
|
||||||
|
uvicorn>=0.15.0,<0.16.0
|
||||||
|
SQLAlchemy==1.3.15
|
||||||
|
psycopg2==2.9.3
|
||||||
|
matplotlib==3.5.0
|
||||||
|
numpy==1.21.4
|
||||||
|
pandas==1.3.5
|
||||||
|
typing
|
||||||
|
kafka-python==2.0.2
|
||||||
|
openpyxl==3.0.7
|
||||||
|
aiofiles==0.6.0
|
||||||
|
pyjwt==2.4.0
|
||||||
|
pydantic[email]>=1.8.0,<2.0.0
|
||||||
|
Jinja2>=3.1.2,<4.0.0
|
||||||
|
requests>=2.25.0,<3.0.0
|
||||||
|
python-multipart==0.0.6 # python-multipart 추가
|
BIN
fastapi/app/router/__pycache__/card_service.cpython-38.pyc
Normal file
BIN
fastapi/app/router/__pycache__/card_service.cpython-38.pyc
Normal file
Binary file not shown.
BIN
fastapi/app/router/__pycache__/router_api.cpython-38.pyc
Normal file
BIN
fastapi/app/router/__pycache__/router_api.cpython-38.pyc
Normal file
Binary file not shown.
70
fastapi/app/router/card_service.py
Normal file
70
fastapi/app/router/card_service.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, Header, Body, status, Request
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from fastapi.security import APIKeyHeader
|
||||||
|
from typing import Union, Optional, List
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
from db import models, schemas, crud
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
from kafka import KafkaProducer
|
||||||
|
from fastapi.responses import FileResponse, StreamingResponse
|
||||||
|
import io
|
||||||
|
import openpyxl
|
||||||
|
|
||||||
|
from db.schemas import RawData, RtuGenerator
|
||||||
|
from db.base import get_db
|
||||||
|
from db.models import RawDatas, Raw_data_herit
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
KST = datetime.timezone(datetime.timedelta(hours=9))
|
||||||
|
|
||||||
|
router = APIRouter(
|
||||||
|
prefix="/api/card",
|
||||||
|
tags=["api", "card"],
|
||||||
|
responses={404: {"description": "Not found"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_body(request: Request):
|
||||||
|
return await request.body()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/regist", response_model=schemas.CardExpense)
|
||||||
|
def create_card_expense(card_expense: schemas.CardExpenseCreate, db: Session = Depends(get_db)):
|
||||||
|
return crud.create_card_expense(db=db, card_expense=card_expense)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/excel", response_model=schemas.CardExpense)
|
||||||
|
async def create_excel(payload: schemas.DatePayload, db: Session = Depends(get_db)):
|
||||||
|
results = crud.get_card_expenses_by_date(db, payload.year, payload.month)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
raise HTTPException(status_code=404, detail="No data found for the specified month")
|
||||||
|
|
||||||
|
# Convert results to pandas DataFrame
|
||||||
|
df = pd.DataFrame([dict(result) for result in results])
|
||||||
|
|
||||||
|
# Create an Excel file in memory
|
||||||
|
output = io.BytesIO()
|
||||||
|
writer = pd.ExcelWriter(output, engine='openpyxl')
|
||||||
|
df.to_excel(writer, index=False, sheet_name='Summary')
|
||||||
|
writer.save()
|
||||||
|
output.seek(0)
|
||||||
|
|
||||||
|
# Return the Excel file
|
||||||
|
response = StreamingResponse(output, media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||||
|
response.headers["Content-Disposition"] = "attachment; filename=summary.xlsx"
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
# 테스트 URL
|
||||||
|
@router.post("/test")
|
||||||
|
async def col_raw_data_from_herit(
|
||||||
|
body: bytes = Depends(get_body)
|
||||||
|
):
|
||||||
|
return {
|
||||||
|
"reason": "OK2",
|
||||||
|
"body": body
|
||||||
|
}
|
57
fastapi/app/router/image_api.py
Normal file
57
fastapi/app/router/image_api.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, Header, Body, status, Request
|
||||||
|
# from db import crud_main
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from fastapi.security import APIKeyHeader
|
||||||
|
from typing import Union, Optional, List
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
from db import models, schemas
|
||||||
|
import datetime
|
||||||
|
from fastapi.responses import FileResponse
|
||||||
|
import os
|
||||||
|
from process.logger import logger
|
||||||
|
|
||||||
|
from db.base import get_db
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
KST = datetime.timezone(datetime.timedelta(hours=9))
|
||||||
|
|
||||||
|
router = APIRouter(
|
||||||
|
prefix="/images",
|
||||||
|
tags=["main"],
|
||||||
|
responses={404: {"description": "Not found"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_body(request: Request):
|
||||||
|
return await request.body()
|
||||||
|
|
||||||
|
|
||||||
|
# 이미지 제공
|
||||||
|
@router.post("/{path1}/{path2}/{path3}/{file_name}")
|
||||||
|
async def get_image(path1: str, path2: str, path3: str, file_name: str):
|
||||||
|
# 이미지 파일 경로 설정 (예: 서버의 특정 디렉토리)
|
||||||
|
image_path = os.path.join("images", path1, path2, path3, file_name)
|
||||||
|
|
||||||
|
# 이미지 파일이 존재하는지 확인
|
||||||
|
if not os.path.exists(image_path):
|
||||||
|
return {"error": "Image not found"}
|
||||||
|
|
||||||
|
# FileResponse를 사용하여 이미지 파일 반환
|
||||||
|
return FileResponse(image_path)
|
||||||
|
|
||||||
|
|
||||||
|
# 이미지 제공
|
||||||
|
@router.get("/{path1}/{path2}/{path3}/{file_name}")
|
||||||
|
async def get_image1(path1: str, path2: str, path3: str, file_name: str):
|
||||||
|
# if path1 != 'email':
|
||||||
|
# return {"error": "path is not allow"}
|
||||||
|
# 이미지 파일 경로 설정 (예: 서버의 특정 디렉토리)
|
||||||
|
image_path = os.path.join("images", path1, path2, path3, file_name)
|
||||||
|
|
||||||
|
# 이미지 파일이 존재하는지 확인
|
||||||
|
if not os.path.exists(image_path):
|
||||||
|
return {"error": "Image not found"}
|
||||||
|
|
||||||
|
# FileResponse를 사용하여 이미지 파일 반환
|
||||||
|
return FileResponse(image_path)
|
557
fastapi/app/router/manage_user_api.py
Normal file
557
fastapi/app/router/manage_user_api.py
Normal file
@ -0,0 +1,557 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, Header, Body, status, Request, File, UploadFile, Form
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from fastapi.security import APIKeyHeader
|
||||||
|
from typing import Union, Optional, List
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
from db import models, schemas, crud
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
from kafka import KafkaProducer
|
||||||
|
from fastapi.responses import FileResponse, StreamingResponse
|
||||||
|
import os
|
||||||
|
import openpyxl
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
from db.schemas import RawData, RtuGenerator
|
||||||
|
from db.base import get_db
|
||||||
|
|
||||||
|
from process.logger import logger
|
||||||
|
|
||||||
|
from process.certification import cert_process
|
||||||
|
from process.response import response
|
||||||
|
from process.user import manage_user
|
||||||
|
from process.user import manage_user_pattern
|
||||||
|
from process.email import email_user
|
||||||
|
|
||||||
|
KST = datetime.timezone(datetime.timedelta(hours=9))
|
||||||
|
|
||||||
|
router = APIRouter(
|
||||||
|
prefix="/user",
|
||||||
|
tags=["user"],
|
||||||
|
responses={404: {"description": "Not found"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_body(request: Request):
|
||||||
|
return await request.body()
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 로그인
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/login")
|
||||||
|
async def login(request: Request, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
# body에서 ID, PW 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
user_id = body['user_id']
|
||||||
|
user_pw = body['user_pw']
|
||||||
|
|
||||||
|
# ID, PW로 로그인 시도
|
||||||
|
login_result = await manage_user.do_login(user_id= user_id, user_pw= user_pw, db=db)
|
||||||
|
|
||||||
|
# 로그인 결과에 따른 응답
|
||||||
|
if login_result['result'] == 'OK':
|
||||||
|
user_seq = (login_result['user_seq'])[0]
|
||||||
|
# 마지막 로그인 시간 업데이트
|
||||||
|
last_login_dt_result = await manage_user.update_last_login_dt(user_seq=user_seq, db=db)
|
||||||
|
if last_login_dt_result['result'] == 'OK':
|
||||||
|
auth_token = {
|
||||||
|
"renew_yn": 'Y',
|
||||||
|
"token": cert_process.create_jwt(user_seq=user_seq, period=3600)
|
||||||
|
}
|
||||||
|
return await response.ok_res(auth_token=auth_token, data={}, db=db)
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 실패', msg_content='로그인 정보 업데이트에 실패했습니다.', data={})
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 실패', msg_content='회원정보를 다시 확인해주세요.', data={})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /user/login\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 에러', msg_content='로그인 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 아이디 찾기
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/find/id")
|
||||||
|
async def find_id(request: Request, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
# body에서 ID, PW 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
nickname = body['nickname']
|
||||||
|
user_email = body['user_email']
|
||||||
|
|
||||||
|
# 닉네임 패턴 검증
|
||||||
|
if not manage_user_pattern.validate_nickname(nickname=nickname):
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이름 확인', msg_content='이름을 정확히 입력해주세요.', data={})
|
||||||
|
# 이메일 패턴 검증
|
||||||
|
if not manage_user_pattern.validate_user_email(user_email=user_email):
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 확인', msg_content='이메일 주소를 정확히 입력해주세요.', data={})
|
||||||
|
|
||||||
|
# 닉네임, EMAIL로 아이디 찾기 시도
|
||||||
|
find_id_result = await manage_user.find_id_by_name_email(nickname=nickname, user_email=user_email, db=db)
|
||||||
|
|
||||||
|
# 아이디 찾기 시도 결과에 따른 응답
|
||||||
|
if find_id_result['result'] == 'OK':
|
||||||
|
# user_seq, user_id 확인
|
||||||
|
user_seq = find_id_result['user_seq']
|
||||||
|
user_id = find_id_result['user_id']
|
||||||
|
# ID 마스킹 처리
|
||||||
|
user_id = user_id[:int(len(user_id)/2)] + ''.join(['*' for _ in range(int(len(user_id)/2))])
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"auth": cert_process.create_jwt(user_seq=user_seq, period=600),
|
||||||
|
"user_id": user_id,
|
||||||
|
}
|
||||||
|
return await response.ok_res(auth_token=auth_token, data=data, db=db)
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='아이디 찾기 실패', msg_content='회원정보를 다시 확인해주세요.', data={})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /user/find/id\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 에러', msg_content='로그인 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 아이디 이메일 전송
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/find/id/full")
|
||||||
|
async def find_receive_by_eamil(request: Request, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
# body에서 ID, PW 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
auth = body['auth']
|
||||||
|
auth_data = cert_process.decode_jwt(auth)
|
||||||
|
if auth_data['result'] == 'OK':
|
||||||
|
auth_data = auth_data['data']
|
||||||
|
user_seq = auth_data['user_seq']
|
||||||
|
exp = auth_data['exp']
|
||||||
|
iat = auth_data['iat']
|
||||||
|
current_time = int(time.time())
|
||||||
|
if not(iat <= current_time and current_time <= exp):
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 발송 실패', msg_content='토큰 유효기간이 만료되었습니다.', data={})
|
||||||
|
else:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
|
||||||
|
# user_seq로 아이디 찾기 시도
|
||||||
|
find_id_result = await manage_user.find_id_by_user_seq(user_seq=user_seq, db=db)
|
||||||
|
|
||||||
|
# 아이디 찾기 시도 결과에 따른 응답
|
||||||
|
if find_id_result['result'] == 'OK':
|
||||||
|
# user_id, user_email 확인
|
||||||
|
user_id = find_id_result['user_id']
|
||||||
|
user_email = find_id_result['user_email']
|
||||||
|
# 이메일 발송 3회 이상 됐는지 확인
|
||||||
|
send_email_cnt_result = await manage_user.select_send_email_cnt(user_email=user_email, db=db)
|
||||||
|
if send_email_cnt_result['result'] == 'OK':
|
||||||
|
send_email_cnt = send_email_cnt_result['send_email_cnt']
|
||||||
|
if send_email_cnt >= 3:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 발송 실패', msg_content='이메일 발송은 하루 3번까지 가능합니다.', data={})
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 발송 실패', msg_content='이메일 발송 실패했습니다.', data={})
|
||||||
|
# 이메일 발송 인증 내역 입력
|
||||||
|
insert_email_cert_result = await manage_user.insert_send_email_info(user_seq=user_seq, cert_type='email', user_info=user_email, cert_code=user_id, db=db)
|
||||||
|
if insert_email_cert_result['result'] == 'FAIL':
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 발송 실패', msg_content='이메일 발송 실패했습니다.', data={})
|
||||||
|
# 이메일로 유저 아이디 발송해주기
|
||||||
|
send_email_result = email_user.email_find_id(user_email=user_email, info=user_id)
|
||||||
|
if send_email_result['result'] == 'OK':
|
||||||
|
return await response.ok_res(auth_token=auth_token, data={"send_email_result": True}, db=db)
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 발송 실패', msg_content=send_email_result['msg'], data={})
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 발송 실패', msg_content='이메일 발송 실패했습니다.', data={})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /user/find/id/full\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='아이디 찾기 에러', msg_content='아이디 찾기 이메일 발송 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 비밀번호 찾기
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/find/password")
|
||||||
|
async def find_pw(request: Request, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
# body에서 ID, PW 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
user_id = body['user_id']
|
||||||
|
user_email = body['user_email']
|
||||||
|
# 아이디 패턴 검증
|
||||||
|
if not manage_user_pattern.validate_user_id(user_id=user_id):
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='아이디 확인', msg_content='아이디를 정확히 입력해주세요.', data={})
|
||||||
|
# 이메일 패턴 검증
|
||||||
|
if not manage_user_pattern.validate_user_email(user_email=user_email):
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 확인', msg_content='이메일 주소를 정확히 입력해주세요.', data={})
|
||||||
|
# 이메일 발송 3회 이상 됐는지 확인
|
||||||
|
send_email_cnt_result = await manage_user.select_send_email_cnt(user_email=user_email, db=db)
|
||||||
|
if send_email_cnt_result['result'] == 'OK':
|
||||||
|
send_email_cnt = send_email_cnt_result['send_email_cnt']
|
||||||
|
if send_email_cnt >= 3:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 발송 실패', msg_content='이메일 발송은 하루 3번까지 가능합니다.', data={})
|
||||||
|
# ID, EMAIL로 비밀번호 찾기 시도
|
||||||
|
find_password_result = await manage_user.find_password_by_id_email(user_id= user_id, user_email= user_email, db=db)
|
||||||
|
logger.debug(f"test1")
|
||||||
|
|
||||||
|
# 비밀번호 찾기 시도 결과에 따른 응답
|
||||||
|
if find_password_result['result'] == 'OK':
|
||||||
|
# user_seq 확인, 신규 PW&SOLT 생성하기
|
||||||
|
user_seq = (find_password_result['user_seq'])[0]
|
||||||
|
new_pw = generate_random_string(20)
|
||||||
|
new_solt = generate_random_string(10)
|
||||||
|
# DB에 신규로 생성된 비밀번호 업데이트 하기
|
||||||
|
update_pw_result = await manage_user.update_new_password(user_seq=user_seq, new_pw=new_pw, new_solt=new_solt, db=db)
|
||||||
|
logger.debug(f"test2")
|
||||||
|
if update_pw_result['result'] == 'OK':
|
||||||
|
# DB 업데이트가 성공하면 이메일로 유저 임시 비밀번호 발송해주기
|
||||||
|
send_email_result = email_user.email_find_password(user_email=user_email, info=new_pw)
|
||||||
|
logger.debug(f"test3")
|
||||||
|
if send_email_result['result'] == 'OK':
|
||||||
|
return await response.ok_res(auth_token=auth_token, data={"send_email_result": send_email_result}, db=db)
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이메일 발송 실패', msg_content=send_email_result['msg'], data={})
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='비밀번호 업데이트 실패', msg_content='임시 비밀번호 업데이트에 실패했습니다.', data={})
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='비밀번호 찾기 실패', msg_content='회원정보를 다시 확인해주세요.', data={})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /user/find/password\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 에러', msg_content='로그인 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 회원가입
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/signup")
|
||||||
|
async def signup(request: Request, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
# body에서 ID 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
# user_id 유효성 검사
|
||||||
|
is_valid_user_id = await manage_user_pattern.is_valid_user_id_by_user_id(user_id=body['user_id'], db=db)
|
||||||
|
fail_msg = None if is_valid_user_id else '아이디가 이미 존재합니다.'
|
||||||
|
if fail_msg is not None:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='회원가입 실패', msg_content=fail_msg, data={})
|
||||||
|
|
||||||
|
# 필수 입력항목 패턴 확인
|
||||||
|
mandatory_list_verify_msg = manage_user_pattern.check_mandatory_insert_list(user_info=body)
|
||||||
|
if mandatory_list_verify_msg is not None:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='회원가입 실패', msg_content=mandatory_list_verify_msg, data={})
|
||||||
|
|
||||||
|
# 회원가입 DB 입력
|
||||||
|
body['user_pw_solt'] = generate_random_string(10) # 비밀번호 SOLT 생성
|
||||||
|
insert_new_user_result = await manage_user.insert_new_user(user_info=body, db=db)
|
||||||
|
|
||||||
|
if insert_new_user_result['result'] == 'OK':
|
||||||
|
return await response.ok_res(auth_token=auth_token, data={}, db=db)
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='회원가입 실패', msg_content='회원가입에 실패했습니다. 입력하신 정보를 다시 확인해주세요.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /user/signup\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 에러', msg_content='로그인 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 프로필 이미지 등록
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/update/profile/img")
|
||||||
|
async def update_profile_img(request: Request, body: str = Form(...), file: UploadFile = File(...), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
if auth_token['result'] == 'OK':
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
elif auth_token['result'] == 'TOKEN_EXPIRED':
|
||||||
|
raise token_expired_return_process(auth_token['msg'])
|
||||||
|
else:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
|
||||||
|
# body에서 ID 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
# 파일 읽기
|
||||||
|
content = await file.read()
|
||||||
|
# 파일 크기 제한
|
||||||
|
MAX_FILE_SIZE = 5 * 1024 * 1024 # 5MB
|
||||||
|
file_size = len(content) # 파일 크기 확인
|
||||||
|
if file_size > MAX_FILE_SIZE:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이미지 업로드 실패', msg_content='이미지 파일 크기는 5MB까지 가능합니다.', data={})
|
||||||
|
|
||||||
|
# body에 들어있는 이미지명 처리
|
||||||
|
user_seq_result = cert_process.get_user_seq_by_token(auth_token['token'])
|
||||||
|
if user_seq_result['result'] == 'OK':
|
||||||
|
# user_seq 추출
|
||||||
|
user_seq = user_seq_result['data']['user_seq']
|
||||||
|
# 현재 시간 가져오기 YYYYMMDDHHMMSSsss
|
||||||
|
now_time = get_now_time_str()
|
||||||
|
# 확장자 확인하고 텍스트 만들기
|
||||||
|
extension = os.path.splitext(file.filename)[1].lower()
|
||||||
|
if extension not in ['.jpg', 'jpeg', '.png', 'bmp']:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이미지 업로드 실패', msg_content='지원하지 않는 확장자입니다.', data={})
|
||||||
|
# IMG_(user_seq)_현재시간 으로 파일명 만들기
|
||||||
|
file_name = f"IMG_{user_seq}_{now_time}{extension}"
|
||||||
|
# 파일 업로드 코드 시작
|
||||||
|
# 경로 존재하지 않으면 생성하기
|
||||||
|
UPLOAD_DIRECTORY = f"images/user/temp_dir/profile_img/"
|
||||||
|
if not os.path.exists(UPLOAD_DIRECTORY):
|
||||||
|
os.makedirs(UPLOAD_DIRECTORY)
|
||||||
|
# 파일 저장 경로 생성
|
||||||
|
file_path = os.path.join(UPLOAD_DIRECTORY, file_name)
|
||||||
|
# 파일을 서버에 저장
|
||||||
|
with open(file_path, "wb") as buffer:
|
||||||
|
buffer.write(content)
|
||||||
|
# 실제로 잘 저장됐는지 확인
|
||||||
|
if os.path.exists(file_path) and os.path.getsize(file_path) > 0:
|
||||||
|
return await response.ok_res(auth_token=auth_token, data={"img_src": '/'+file_path}, db=db)
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='이미지 업로드 실패', msg_content='이미지 업로드에 실패했습니다.', data={})
|
||||||
|
else:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /user/update/profile/img\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 에러', msg_content='로그인 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 내 정보 가져오기
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/myinfo")
|
||||||
|
async def update_user_info(request: Request, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
if auth_token['result'] == 'OK':
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
elif auth_token['result'] == 'TOKEN_EXPIRED':
|
||||||
|
raise token_expired_return_process(auth_token['msg'])
|
||||||
|
else:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
# body에서 ID 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
user_seq_result = cert_process.get_user_seq_by_token(token=auth_token['token'])
|
||||||
|
if user_seq_result["result"] == 'OK':
|
||||||
|
user_seq = user_seq_result['data']['user_seq']
|
||||||
|
# 유저 정보 가져오기
|
||||||
|
user_data = await manage_user.get_my_info_by_user_seq(user_seq=user_seq, db=db)
|
||||||
|
|
||||||
|
if user_data['result'] == 'OK':
|
||||||
|
return await response.ok_res(auth_token=auth_token, data=user_data['data'], db=db)
|
||||||
|
|
||||||
|
logger.error(f"request error. URL: /user/myinfo\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='마이페이지 내정보 가져오기 에러', msg_content='마이페이지 데이터 조회중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /user/myinfo\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 에러', msg_content='로그인 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 유저 정보 수정하기
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/update/user/info")
|
||||||
|
async def update_user_info(request: Request, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
if auth_token['result'] == 'OK':
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
elif auth_token['result'] == 'TOKEN_EXPIRED':
|
||||||
|
raise token_expired_return_process(auth_token['msg'])
|
||||||
|
else:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
# body에서 ID 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
user_seq_result = cert_process.get_user_seq_by_token(token=auth_token['token'])
|
||||||
|
if user_seq_result["result"] == 'OK':
|
||||||
|
user_seq = user_seq_result['data']['user_seq']
|
||||||
|
body['user_seq'] = user_seq
|
||||||
|
# 현재 비밀번호 일치하는지 확인
|
||||||
|
current_pw_correct_result = await manage_user.check_current_user_pw(user_seq=user_seq, user_pw=body['user_pw'], db=db)
|
||||||
|
if current_pw_correct_result['result'] == 'FAIL':
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='현재 비밀번호 인증 실패', msg_content='현재 비밀번호가 일치하지 않습니다.', data={})
|
||||||
|
|
||||||
|
# 입력된 값 패턴 검증
|
||||||
|
new_data_pattern_result_msg = manage_user_pattern.check_new_data(user_info=body)
|
||||||
|
if new_data_pattern_result_msg is not None:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='회원정보 수정 실패', msg_content=new_data_pattern_result_msg, data={})
|
||||||
|
|
||||||
|
# 유저 정보 가져오기
|
||||||
|
user_data = await manage_user.get_my_info_by_user_seq(user_seq=user_seq, db=db)
|
||||||
|
if user_data['result'] == 'OK':
|
||||||
|
user_data = user_data['data']
|
||||||
|
else:
|
||||||
|
return response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='정보 가져오기 에러', msg_content='유저 정보 가져오기에 실패했습니다.', data={})
|
||||||
|
# 변경된 항목들 확인하기
|
||||||
|
# user_pw 변경 됐는지 확인
|
||||||
|
if body['user_pw_change_yn'] == 'Y':
|
||||||
|
body['user_pw_solt'] = generate_random_string(10)
|
||||||
|
# nickname 변경 됐는지 확인
|
||||||
|
body['nickname'] = body['nickname'] if body['nickname'] != user_data['nickname'] else user_data['nickname']
|
||||||
|
# user_email 변경 됐는지 확인
|
||||||
|
body['user_email'] = body['user_email'] if body['user_email'] != user_data['user_email'] else user_data['user_email']
|
||||||
|
# department 변경 됐는지 확인
|
||||||
|
body['department'] = body['department'] if body['department'] != user_data['department'] else user_data['department']
|
||||||
|
# profile_img 변경 됐는지 확인
|
||||||
|
body['profile_img'] = body['profile_img'] if body['profile_img'] != user_data['profile_img'] else user_data['profile_img']
|
||||||
|
# profile_img 변경 됐는지 확인
|
||||||
|
body['introduce_myself'] = body['introduce_myself'] if body['introduce_myself'] != user_data['introduce_myself'] else user_data['introduce_myself']
|
||||||
|
|
||||||
|
# 변경된 정보에 대해서만 업데이트하기
|
||||||
|
update_result = await manage_user.update_user_info(user_info=body, db=db)
|
||||||
|
if update_result['result'] == 'OK':
|
||||||
|
return await response.ok_res(auth_token=auth_token, data={}, db=db)
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='회원정보 변경 실패', msg_content='회원정보 변경 실패했습니다. 관리자에게 문의해주세요.', data={})
|
||||||
|
else:
|
||||||
|
logger.error(f"request error. URL: /user/update/user/info\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /user/update/user/info\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 에러', msg_content='로그인 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 회원탈퇴
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/withdraw/user")
|
||||||
|
async def withdraw_user(request: Request, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
if auth_token['result'] == 'OK':
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
elif auth_token['result'] == 'TOKEN_EXPIRED':
|
||||||
|
raise token_expired_return_process(auth_token['msg'])
|
||||||
|
else:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
# body에서 ID 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
user_pw = body['user_pw']
|
||||||
|
|
||||||
|
user_seq_result = cert_process.get_user_seq_by_token(token=auth_token['token'])
|
||||||
|
if user_seq_result["result"] == 'OK':
|
||||||
|
user_seq = user_seq_result['data']['user_seq']
|
||||||
|
# 현재 비밀번호 일치하는지 확인
|
||||||
|
current_pw_correct_result = await manage_user.check_current_user_pw(user_seq=user_seq, user_pw=user_pw, db=db)
|
||||||
|
if current_pw_correct_result['result'] == 'OK':
|
||||||
|
# 회원 탈퇴 처리
|
||||||
|
withdraw_result = await manage_user.user_withdraw(user_seq=user_seq, db=db)
|
||||||
|
if withdraw_result['result'] == 'OK':
|
||||||
|
return await response.ok_res(auth_token=auth_token, data={}, db=db)
|
||||||
|
else:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='회원탈퇴 에러', msg_content='회원탈퇴 처리중 에러가 발생헀습니다. 관리자에게 문의해주세요.', data={})
|
||||||
|
else:
|
||||||
|
return await response.fail_res(auth_token=auth_token, auth_type='NOMAL', msg_title='현재 비밀번호 인증 실패', msg_content='현재 비밀번호가 일치하지 않습니다.', data={})
|
||||||
|
else:
|
||||||
|
logger.error(f"request error. URL: /user/withdraw/user\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /user/withdraw/user\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 에러', msg_content='로그인 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 필요한 함수
|
||||||
|
#==================================================================================================
|
||||||
|
# N자 대문자, 소문자, 숫자 조합 랜덤 발생기
|
||||||
|
def generate_random_string(length=10):
|
||||||
|
# 영문 대문자, 소문자, 숫자를 모두 포함한 문자 집합
|
||||||
|
characters = string.ascii_letters + string.digits
|
||||||
|
# 지정된 길이만큼 랜덤하게 선택하여 문자열 생성
|
||||||
|
random_string = ''.join(random.choice(characters) for _ in range(length))
|
||||||
|
return random_string
|
||||||
|
|
||||||
|
|
||||||
|
# N자 숫자 조합 랜덤 발생기
|
||||||
|
def generate_random_number(length=6):
|
||||||
|
characters = string.digits
|
||||||
|
# 지정된 길이만큼 랜덤하게 선택하여 문자열 생성
|
||||||
|
random_string = ''.join(random.choice(characters) for _ in range(length))
|
||||||
|
return random_string
|
||||||
|
|
||||||
|
|
||||||
|
def token_expired_return_process(fail_msg):
|
||||||
|
logger.error(f"request fail: {fail_msg}")
|
||||||
|
return HTTPException(
|
||||||
|
status_code=401,
|
||||||
|
detail=f"{fail_msg}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# 현재 시간 STR로 가져오기
|
||||||
|
def get_now_time_str():
|
||||||
|
# 현재 시간 가져오기
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
|
# YYYYMMDDHHMMSSsss 형식으로 포맷
|
||||||
|
formatted_time = now.strftime('%Y%m%d%H%M%S%f')[:17]
|
||||||
|
|
||||||
|
return formatted_time
|
93
fastapi/app/router/room_score_api.py
Normal file
93
fastapi/app/router/room_score_api.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, Header, Body, status, Request
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from fastapi.security import APIKeyHeader
|
||||||
|
from typing import Union, Optional, List
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
from db import models, schemas, crud
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
from kafka import KafkaProducer
|
||||||
|
from fastapi.responses import FileResponse, StreamingResponse
|
||||||
|
import io
|
||||||
|
import openpyxl
|
||||||
|
import time
|
||||||
|
|
||||||
|
from db.schemas import RawData, RtuGenerator
|
||||||
|
from db.base import get_db
|
||||||
|
from db.models import RawDatas, Raw_data_herit
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from process.logger import logger
|
||||||
|
|
||||||
|
from process.certification import cert_process
|
||||||
|
from process.response import response
|
||||||
|
from process.room import room_score
|
||||||
|
|
||||||
|
KST = datetime.timezone(datetime.timedelta(hours=9))
|
||||||
|
|
||||||
|
router = APIRouter(
|
||||||
|
prefix="/room/score",
|
||||||
|
tags=["room", "score"],
|
||||||
|
responses={404: {"description": "Not found"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_body(request: Request):
|
||||||
|
return await request.body()
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 방 생성하기
|
||||||
|
#==================================================================================================
|
||||||
|
@router.post("/create/room")
|
||||||
|
async def login(request: Request, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# 인증서 갱신
|
||||||
|
auth_token = cert_process.renew_cert(request=request)
|
||||||
|
if auth_token['result'] == 'OK':
|
||||||
|
auth_token = auth_token['data']
|
||||||
|
elif auth_token['result'] == 'TOKEN_EXPIRED':
|
||||||
|
raise token_expired_return_process(auth_token['msg'])
|
||||||
|
else:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
# body에서 ID 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='데이터 에러', msg_content='데이터 처리 장애가 발생했습니다. 요청정보를 정확히 입력했는지 확인해주세요.', data={})
|
||||||
|
|
||||||
|
user_seq_result = cert_process.get_user_seq_by_token(token=auth_token['token'])
|
||||||
|
if user_seq_result["result"] == 'OK':
|
||||||
|
user_seq = user_seq_result['data']['user_seq']
|
||||||
|
body['user_seq'] = user_seq
|
||||||
|
# 방 생성
|
||||||
|
create_room_result = await room_score.create_room(data=body)
|
||||||
|
if create_room_result['result'] == 'OK':
|
||||||
|
return await response.ok_res(auth_token=auth_token, data={}, db=db)
|
||||||
|
else:
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='방생성 에러', msg_content='방 생성중 에러가 발생했습니다. 관리자에게 문의해주세요.', data={})
|
||||||
|
else:
|
||||||
|
logger.error(f"request error. URL: /user/withdraw/user\nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='토큰 에러', msg_content='토큰 정보가 정확하지 않습니다.', data={})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"request error. URL: /room/score/ \nerror message: {e}")
|
||||||
|
return await response.error_res(auth_token=auth_token, auth_type='NOMAL', msg_title='로그인 에러', msg_content='로그인 처리중 에러가 발생했습니다.', data={})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#==================================================================================================
|
||||||
|
# 필요한 함수
|
||||||
|
#==================================================================================================
|
||||||
|
# 401 에러 발생
|
||||||
|
def token_expired_return_process(fail_msg):
|
||||||
|
logger.error(f"request fail: {fail_msg}")
|
||||||
|
return HTTPException(
|
||||||
|
status_code=401,
|
||||||
|
detail=f"{fail_msg}"
|
||||||
|
)
|
||||||
|
|
140
fastapi/app/router/router_api.py
Normal file
140
fastapi/app/router/router_api.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, Header, Body, status, Request
|
||||||
|
from fastapi.security import APIKeyHeader
|
||||||
|
from typing import Union, Optional, List
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
from kafka import KafkaProducer
|
||||||
|
|
||||||
|
KST = datetime.timezone(datetime.timedelta(hours=9))
|
||||||
|
|
||||||
|
router = APIRouter(
|
||||||
|
prefix="/api",
|
||||||
|
tags=["api", "collect"],
|
||||||
|
responses={404: {"description": "Not found"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------
|
||||||
|
# 비정상 요청 로그 기록
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
#2 logger의 level을 가장 낮은 수준인 DEBUG로 설정해둔다.
|
||||||
|
logger.setLevel(logging.ERROR)
|
||||||
|
|
||||||
|
#3 formatter 지정
|
||||||
|
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
|
|
||||||
|
#4 handler instance 생성
|
||||||
|
console = logging.StreamHandler()
|
||||||
|
file_handler = logging.FileHandler(filename="error_request.log")
|
||||||
|
|
||||||
|
#5 handler 별로 다른 level 설정
|
||||||
|
console.setLevel(logging.ERROR)
|
||||||
|
file_handler.setLevel(logging.ERROR)
|
||||||
|
|
||||||
|
#6 handler 출력 format 지정
|
||||||
|
console.setFormatter(formatter)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
#7 logger에 handler 추가
|
||||||
|
logger.addHandler(console)
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
|
||||||
|
KAFKA_IP_DEV = "183.107.250.208:9092"
|
||||||
|
KAFKA_IP = "10.0.1.10:9092"
|
||||||
|
KAFKA_TOPIC = "boryeong-herit"
|
||||||
|
|
||||||
|
api_key_header = APIKeyHeader(name="X-HIT-TransactionId")
|
||||||
|
|
||||||
|
async def get_body(request: Request):
|
||||||
|
return await request.body()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_header(header: str = Depends(api_key_header)):
|
||||||
|
return await header
|
||||||
|
|
||||||
|
|
||||||
|
# 헤더의 토큰 확인하는
|
||||||
|
async def api_token(token: str = Depends(api_key_header)):
|
||||||
|
if len(token) == 0 or token.count('.') != 3:
|
||||||
|
logger.error("The value of 'header[X-HIT-TransactionId]' is not determined by a specific rule.")
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
||||||
|
token = token.split('.')
|
||||||
|
if len(token) != 4 or token[0] !='T' or len(token[3]) != 20:
|
||||||
|
logger.error("The value of 'header[X-HIT-TransactionId]' is not determined by a specific rule.")
|
||||||
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/herit", dependencies=[Depends(api_token)])
|
||||||
|
async def col_raw_data_from_herit(
|
||||||
|
content_Type: Optional[str] = Header(None),
|
||||||
|
X_HIT_TransactionId: Optional[str] = Header(None),
|
||||||
|
content_Length: Optional[str] = Header(None),
|
||||||
|
body: bytes = Depends(get_body)
|
||||||
|
):
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'content_Type': str(content_Type),
|
||||||
|
'X_HIT_TransactionId': str(X_HIT_TransactionId),
|
||||||
|
'content_Length': str(content_Length),
|
||||||
|
'body': str(body.decode("UTF-8"))
|
||||||
|
}
|
||||||
|
message_producer = MessageProducer(KAFKA_IP, KAFKA_TOPIC)
|
||||||
|
# message_producer_dev = MessageProducer(KAFKA_IP_DEV, KAFKA_TOPIC)
|
||||||
|
|
||||||
|
msg = {
|
||||||
|
"data_type": 'data_herit',
|
||||||
|
"data_herit": {
|
||||||
|
"content_type": data['content_Type'],
|
||||||
|
"x_hit_transactionid": data['X_HIT_TransactionId'],
|
||||||
|
"content_length": data['content_Length'],
|
||||||
|
"body": data['body']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = message_producer.send_message(msg)
|
||||||
|
# res_dev = message_producer_dev.send_message(msg)
|
||||||
|
|
||||||
|
if res['status_code'] != status.HTTP_200_OK:
|
||||||
|
return {"reason": "SERVER ERROR"}
|
||||||
|
else :
|
||||||
|
return {"reason": "OK"}
|
||||||
|
|
||||||
|
|
||||||
|
# 테스트 URL
|
||||||
|
@router.post("/test")
|
||||||
|
async def col_raw_data_from_herit(
|
||||||
|
body: bytes = Depends(get_body)
|
||||||
|
):
|
||||||
|
return {
|
||||||
|
"reason": "OK2",
|
||||||
|
"body": body
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------
|
||||||
|
# Kafka로 전달하기위한 클래스
|
||||||
|
class MessageProducer:
|
||||||
|
broker = ""
|
||||||
|
topic = ""
|
||||||
|
producer = None
|
||||||
|
|
||||||
|
def __init__(self, broker, topic):
|
||||||
|
self.broker = broker
|
||||||
|
self.topic = topic
|
||||||
|
self.producer = KafkaProducer(bootstrap_servers=self.broker,
|
||||||
|
value_serializer=lambda x: json.dumps(x).encode('utf-8'),
|
||||||
|
acks=0,
|
||||||
|
api_version=(2,5,0),
|
||||||
|
retries=3
|
||||||
|
)
|
||||||
|
|
||||||
|
def send_message(self, msg):
|
||||||
|
try:
|
||||||
|
future = self.producer.send(self.topic, msg)
|
||||||
|
self.producer.flush() # 비우는 작업
|
||||||
|
future.get(timeout=60)
|
||||||
|
return {'status_code': 200, 'error': None}
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"kafka process error: {e}")
|
||||||
|
return e
|
132
fastapi/app/router/test_service.py
Normal file
132
fastapi/app/router/test_service.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, Header, Body, status, Request
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
import json
|
||||||
|
import datetime
|
||||||
|
import requests
|
||||||
|
import jwt
|
||||||
|
import time
|
||||||
|
from process.logger import logger
|
||||||
|
|
||||||
|
from db.base import get_db
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from common.config import SECRET_KEY
|
||||||
|
|
||||||
|
KST = datetime.timezone(datetime.timedelta(hours=9))
|
||||||
|
|
||||||
|
router = APIRouter(
|
||||||
|
prefix="/test",
|
||||||
|
tags=["test"],
|
||||||
|
responses={404: {"description": "Not found"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_body(request: Request):
|
||||||
|
return await request.body()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# user_seq로 토큰 생성기
|
||||||
|
@router.post("/make/token/{user_seq}")
|
||||||
|
async def make_new_token(user_seq: str, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
user_token = create_jwt(user_seq=int(user_seq), period=3600)
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"data": {
|
||||||
|
"token": user_token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
"msg": e
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# user_seq로 만료된 토큰 생성기
|
||||||
|
@router.post("/make/token/expired/{user_seq}")
|
||||||
|
async def make_new_token_expired(user_seq: str, body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
user_token = create_jwt(user_seq=int(user_seq), period=0)
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"data": {
|
||||||
|
"token": user_token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
"msg": e
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 토큰 정보 확인
|
||||||
|
@router.post("/check/token")
|
||||||
|
async def check_token_data(body: bytes = Depends(get_body), db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
# body에서 ID 추출
|
||||||
|
try:
|
||||||
|
body = json.loads(body)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return logger.error(f"json.loads error: Invalid JSON format in request body\nerror message: {e}")
|
||||||
|
|
||||||
|
token = body['token']
|
||||||
|
token_decode = decode_jwt(token=token)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"result": "OK",
|
||||||
|
"data": {
|
||||||
|
"user_seq": token_decode['user_seq'],
|
||||||
|
"exp": change_time(token_decode['exp']),
|
||||||
|
"iat": change_time(token_decode['iat']),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
"result": "FAIL",
|
||||||
|
"msg": e
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def create_jwt(user_seq: int, period: int):
|
||||||
|
# 현재 시간
|
||||||
|
now = int(time.time())
|
||||||
|
|
||||||
|
# JWT의 페이로드 (클레임)
|
||||||
|
payload = {
|
||||||
|
"user_seq": user_seq,
|
||||||
|
"exp": now + period, # 만료 시간
|
||||||
|
"iat": now, # 토큰 발행 시간
|
||||||
|
}
|
||||||
|
|
||||||
|
# JWT 생성 (HS256 알고리즘 사용)
|
||||||
|
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
|
||||||
|
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
|
def decode_jwt(token: str):
|
||||||
|
try:
|
||||||
|
# 토큰 디코드
|
||||||
|
decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
|
||||||
|
return decoded_payload
|
||||||
|
except jwt.ExpiredSignatureError:
|
||||||
|
return {"error": "Token has expired"}
|
||||||
|
except jwt.InvalidTokenError:
|
||||||
|
return {"error": "Invalid token"}
|
||||||
|
|
||||||
|
|
||||||
|
# 현재 시간 YYYY-MM-DD hh:mm:ss로 변환 함수
|
||||||
|
def change_time(time_data):
|
||||||
|
dt_object = datetime.datetime.fromtimestamp(time_data)
|
||||||
|
formatted_time = dt_object.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
return formatted_time
|
0
fastapi/app/server.crt
Normal file
0
fastapi/app/server.crt
Normal file
0
fastapi/app/server.key
Normal file
0
fastapi/app/server.key
Normal file
4
fastapi/create_image_fastapi.sh
Normal file
4
fastapi/create_image_fastapi.sh
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
sudo docker build -t fastapi:v1 -f ./Dockerfile-fastapi .
|
26
fastapi/docker-compose-fastapi.yml
Normal file
26
fastapi/docker-compose-fastapi.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
api:
|
||||||
|
image: fastapi:v1
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Seoul # 한국 시간대 설정
|
||||||
|
volumes:
|
||||||
|
- /home/ec2-user/eld/card_service/backend/fastapi/app:/app
|
||||||
|
command: >
|
||||||
|
uvicorn main:app --host 0.0.0.0 --port 8098
|
||||||
|
expose:
|
||||||
|
- "8098"
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx:latest
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Seoul # 한국 시간대 설정
|
||||||
|
ports:
|
||||||
|
- "8097:8097" # 외부에서 8097 포트로 접근
|
||||||
|
volumes:
|
||||||
|
- /home/ec2-user/eld/card_service/backend/fastapi/nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
- /home/ec2-user/eld/card_service/backend/fastapi/eld.crt:/etc/nginx/ssl/eld.crt
|
||||||
|
- /home/ec2-user/eld/card_service/backend/fastapi/eld.key:/etc/nginx/ssl/eld.key
|
||||||
|
depends_on:
|
||||||
|
- api
|
35
fastapi/eld.crt
Normal file
35
fastapi/eld.crt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIGCDCCBPCgAwIBAgIQWwEQbfqkdYEA35goU9JMADANBgkqhkiG9w0BAQsFADBM
|
||||||
|
MQswCQYDVQQGEwJMVjENMAsGA1UEBxMEUmlnYTERMA8GA1UEChMIR29HZXRTU0wx
|
||||||
|
GzAZBgNVBAMTEkdvR2V0U1NMIFJTQSBEViBDQTAeFw0yNDA3MjMwMDAwMDBaFw0y
|
||||||
|
NTA3MjMyMzU5NTlaMBYxFDASBgNVBAMTC2VsZHNvZnQuY29tMIIBIjANBgkqhkiG
|
||||||
|
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkYA7IYc330ya/BFBhcG8QQrdKuV5GPV5rh0K
|
||||||
|
NIeCU0yWl5ydYzpXhWuFE/qnQfVRK5g+1jAfBK4UHTZdpdvWzdDlZFkJBiQO0MS2
|
||||||
|
/ujWpTWSEYqalgRxZylTDSOG+KAt+Tpo0ZWv/T6okjW54+J2vi4E/2QGPzi/Dr5c
|
||||||
|
d9hlMBB3VREsmDqDScVnHOlxbMVLf/EgIEP5CbGwdV71/R5Tpcdkr9ubw5s1RdMz
|
||||||
|
GEYfQEJM56zdlAwhG7ucadoIVXRlP6BWmyrk9uZgcDg1waUoFekyCGg/gwhdzoH5
|
||||||
|
xglDwb1tL/b+jxhgm8Cu0Av5GwaD45LSc1B1+AHM99qnoETuKQIDAQABo4IDGjCC
|
||||||
|
AxYwHwYDVR0jBBgwFoAU+ftQxItnu2dk/oMhpqnOP1WEk5kwHQYDVR0OBBYEFLxH
|
||||||
|
VZ+saqBmJr8jEwI3UhYLh4DLMA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAA
|
||||||
|
MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBLBgNVHSAERDBCMDYGCysG
|
||||||
|
AQQBsjEBAgJAMCcwJQYIKwYBBQUHAgEWGWh0dHBzOi8vY3BzLnVzZXJ0cnVzdC5j
|
||||||
|
b20wCAYGZ4EMAQIBMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jcmwudXNlcnRy
|
||||||
|
dXN0LmNvbS9Hb0dldFNTTFJTQURWQ0EuY3JsMG8GCCsGAQUFBwEBBGMwYTA4Bggr
|
||||||
|
BgEFBQcwAoYsaHR0cDovL2NydC51c2VydHJ1c3QuY29tL0dvR2V0U1NMUlNBRFZD
|
||||||
|
QS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wFgYD
|
||||||
|
VR0RBA8wDYILZWxkc29mdC5jb20wggGABgorBgEEAdZ5AgQCBIIBcASCAWwBagB2
|
||||||
|
AN3cyjSV1+EWBeeVMvrHn/g9HFDf2wA6FBJ2Ciysu8gqAAABkN5FaiIAAAQDAEcw
|
||||||
|
RQIgHTbwtmQN6xLMETdF+8eqQ7AKqXO8N2f+bD7Fx7tzlo4CIQDc3kmta7RofW+L
|
||||||
|
jZxQtI4p3b4hFUFCDw85ibQgCSpH6QB3AA3h8jAr0w3BQGISCepVLvxHdHyx1+kw
|
||||||
|
7w5CHrR+Tqo0AAABkN5Fae8AAAQDAEgwRgIhAI2ZgaTuuAjcfrg6o2KeX+O8ga8e
|
||||||
|
tGY7+NZRt/yRXuyXAiEA5bEkR2LchqMV3vYr5Eo/7nKm8eTXNBhduxNIPxNzRoAA
|
||||||
|
dwAS8U40vVNyTIQGGcOPP3oT+Oe1YoeInG0wBYTr5YYmOgAAAZDeRWnNAAAEAwBI
|
||||||
|
MEYCIQDT4+jvqlZboMs6AeqxZRu0TyKB/zA5UzGfJdaCaWQ8iAIhAKgg7Q+W0sd/
|
||||||
|
Gxaa2pHXpHSxP3sfkT9o5Br8S4pILca3MA0GCSqGSIb3DQEBCwUAA4IBAQA51hYf
|
||||||
|
mw+kueMcWmPUlUikrK0/KuLpSfWPvHOj+r84Y5AS9JxZmi9b+9W18p1VwO0YXR6U
|
||||||
|
ho4PyHjuIviT2LviAWyvRTFc9Il+e920+RyMiAdfQ/Af15xdcAJOjSMlZu3xkqvW
|
||||||
|
gkgE1kPvIQevqCmEEzUqsUVEn7ftoDcT9SEJBpLFwB6FAFBfw0pvJ0qUr9LAin4p
|
||||||
|
bklWGaY2laI2py7MXZNUO35rahA0DNS/Y2sY3TbH/WXilJ/sbJ9sO83/XC2T7vgC
|
||||||
|
Le2cv9gNFJPiTW++OROxybXzarwIwWFHO7aPc5Ehv9U54/XlzBIzrRsJuvtXkc0I
|
||||||
|
0nuiAfpjOom89Z1W
|
||||||
|
-----END CERTIFICATE-----
|
17
fastapi/eld.csr
Normal file
17
fastapi/eld.csr
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIICrzCCAZcCAQAwbDEUMBIGA1UEAwwLZWxkc29mdC5jb20xHzAdBgNVBAoMFuyj
|
||||||
|
vOyLne2ajOyCrCDsnbTsl5jrlJQxEjAQBgNVBAgMCeuMgOyghOyLnDESMBAGA1UE
|
||||||
|
BwwJ7Jyg7ISx6rWsMQswCQYDVQQGEwJLUjCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||||
|
ADCCAQoCggEBAJGAOyGHN99MmvwRQYXBvEEK3SrleRj1ea4dCjSHglNMlpecnWM6
|
||||||
|
V4VrhRP6p0H1USuYPtYwHwSuFB02XaXb1s3Q5WRZCQYkDtDEtv7o1qU1khGKmpYE
|
||||||
|
cWcpUw0jhvigLfk6aNGVr/0+qJI1uePidr4uBP9kBj84vw6+XHfYZTAQd1URLJg6
|
||||||
|
g0nFZxzpcWzFS3/xICBD+QmxsHVe9f0eU6XHZK/bm8ObNUXTMxhGH0BCTOes3ZQM
|
||||||
|
IRu7nGnaCFV0ZT+gVpsq5PbmYHA4NcGlKBXpMghoP4MIXc6B+cYJQ8G9bS/2/o8Y
|
||||||
|
YJvArtAL+RsGg+OS0nNQdfgBzPfap6BE7ikCAwEAATANBgkqhkiG9w0BAQsFAAOC
|
||||||
|
AQEAZhOyuc//t8Vp+Iog/C12fFSIB4baHBS/GV3C/xm1gvmn/Vl8ynaswPA3ei0U
|
||||||
|
rvoXidZHYt7gdcBq1DlPDGxbnEwNYZhNFR0Sv622EO9MrsiUsje6yWm2xmCgM8zw
|
||||||
|
ChhifmEulSBEFcOQGi9eKA19jJHKEqRx5QhlZHsRdVD+glxyESpOM0cfUXGsYXhn
|
||||||
|
Pvm9EvlZfYyoVBRjIwO3CIKSaGsgg2FKUKnmgMo28BKVPYM1/gNC47ozhwMOgDrO
|
||||||
|
fg1KZ3CtOMn3ZG7OiN2HamnBNKQDDp6mZJT1aoq1GsENYFNJUfW768G2rmAUGNfw
|
||||||
|
My3PBjidoiuChvimG1Fqpnioxw==
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
27
fastapi/eld.key
Normal file
27
fastapi/eld.key
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEAkYA7IYc330ya/BFBhcG8QQrdKuV5GPV5rh0KNIeCU0yWl5yd
|
||||||
|
YzpXhWuFE/qnQfVRK5g+1jAfBK4UHTZdpdvWzdDlZFkJBiQO0MS2/ujWpTWSEYqa
|
||||||
|
lgRxZylTDSOG+KAt+Tpo0ZWv/T6okjW54+J2vi4E/2QGPzi/Dr5cd9hlMBB3VREs
|
||||||
|
mDqDScVnHOlxbMVLf/EgIEP5CbGwdV71/R5Tpcdkr9ubw5s1RdMzGEYfQEJM56zd
|
||||||
|
lAwhG7ucadoIVXRlP6BWmyrk9uZgcDg1waUoFekyCGg/gwhdzoH5xglDwb1tL/b+
|
||||||
|
jxhgm8Cu0Av5GwaD45LSc1B1+AHM99qnoETuKQIDAQABAoIBAAhxYU0CYoe4s2AX
|
||||||
|
1L5hFe5MxfVqpCaifOVxbjGK4PE6Nx1UU04qGSDG8s2MXIb/aA7AanoFiBE+lDB3
|
||||||
|
QoMgsPPXsK3sXDGQ51KuLYO4aVckFwYhTbPRjW6L53OyWW9FJTHKdcFenxwR9hho
|
||||||
|
2XDrp9IEi9nxeQrTXUPK4ET8h6+cmHgEDEXJ18xyn3EMrXbBTOBaAGJwdA/kE947
|
||||||
|
o85Yu+o3sKA+3JsuuMijSy2XSqvslNNeyOvlcFcq88eDY5pXuDGbRQPnIQe3t0Fc
|
||||||
|
DMtIF2E2jOtIbMuBdcdZaUduiPqVRT3ojW2LaDc58H7+piAgBUVGJCLuqIgujBa0
|
||||||
|
t3n9M3ECgYEA1oe+oVG8+OVvcg9IJyQmnZOIR3hopuDvZOCYuSOsOS8DMo+p066z
|
||||||
|
6rSKpvtIcJJ8l0mM8Rw4WifEbEYOgkt9yGVK80mMI2BkSwmq5U4hOWg/xxqATTz5
|
||||||
|
PBgz0bBwZ6ZgW/3RD+jWybX3vy+beifAL3v52q+hnZdZf1q1rBYYWXECgYEAraB/
|
||||||
|
C/a4RO0LygrncUgn9MX4mGe2zRVAszZcbcX2iG4o1mseoZ16HO1ipS4Q/kI/znpY
|
||||||
|
r7XT4QgF98Fdkcl8MYKP+snUv9TG7zOFiYOe0JeaafBuukMDPUqeQXk/d9rSrJg9
|
||||||
|
lqiw1LYYyxPnh8aTEfwMBPqaRhLiMeD63PsHRDkCgYEAzwflUi1dnx1b9ckFqrBa
|
||||||
|
i8tqwv5SkGmW3dVZzaG9fNn/zfWSwPRiMOjWvdrWx7y2fBHA8JZ5U5f5GTxqmBde
|
||||||
|
ZdxK/opFsYY+g6PqxqwlqA8RLYZHt0JWjEYXDA+oCn8nkt9ZuG7NiZAQbPL2qmZe
|
||||||
|
M/UC5KaF413CQwM5O79+9CECgYBT/D+YNOabiKJcP/wGAuY48441gm2dNDuQtKnu
|
||||||
|
+4QuKEMevMAbYwZPedBuoCLeKoOcx/egPu7Xej8QwfsV6wVlGYe1wu1jQXRc/moI
|
||||||
|
w58NvVeXCRM2i/XELxTwDMtTmYiwrg+UkdK/gbnqeZ1UQwye9XGG8wWvAbFieTY/
|
||||||
|
sDmqmQKBgQCUyu98Gpatoe8MdyHZf9h+QfYcUkpLuhJOvWCGQt9QyfiIal5eleXG
|
||||||
|
YPNaQBk3GtSPyuB1nDrjtHaIlyw1ScjRCfzth3VJvwWnqz9y7XP2pow2+TnY0Jco
|
||||||
|
lvMRY9n55TgAPdTdgHshUyDRAM1/aUbiBIPPOP/XAcmONZ1lpayZ7A==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
22
fastapi/nginx.conf
Normal file
22
fastapi/nginx.conf
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
events {
|
||||||
|
worker_connections 2048;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 8097 ssl;
|
||||||
|
server_name eldsoft.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/nginx/ssl/eld.crt;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/eld.key;
|
||||||
|
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://api:8098;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
fastapi/restart.sh
Normal file
10
fastapi/restart.sh
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
docker-compose -f /home/ec2-user/eld/card_service/backend/fastapi/docker-compose-fastapi.yml down
|
||||||
|
docker-compose -f /home/ec2-user/eld/card_service/backend/fastapi/docker-compose-fastapi.yml up -d
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
docker-compose -f /home/ec2-user/eld/card_service/backend/fastapi/docker-compose-fastapi.yml ps
|
||||||
|
|
44
kafka/Dockerfile-kafka
Normal file
44
kafka/Dockerfile-kafka
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
FROM azul/zulu-openjdk-alpine:8u292-8.54.0.21
|
||||||
|
|
||||||
|
ARG kafka_version=2.7.1
|
||||||
|
ARG scala_version=2.13
|
||||||
|
ARG glibc_version=2.31-r0
|
||||||
|
ARG vcs_ref=unspecified
|
||||||
|
ARG build_date=unspecified
|
||||||
|
|
||||||
|
LABEL org.label-schema.name="kafka" \
|
||||||
|
org.label-schema.description="Apache Kafka" \
|
||||||
|
org.label-schema.build-date="${build_date}" \
|
||||||
|
org.label-schema.vcs-url="https://github.com/wurstmeister/kafka-docker" \
|
||||||
|
org.label-schema.vcs-ref="${vcs_ref}" \
|
||||||
|
org.label-schema.version="${scala_version}_${kafka_version}" \
|
||||||
|
org.label-schema.schema-version="1.0" \
|
||||||
|
maintainer="wurstmeister"
|
||||||
|
|
||||||
|
ENV KAFKA_VERSION=$kafka_version \
|
||||||
|
SCALA_VERSION=$scala_version \
|
||||||
|
KAFKA_HOME=/opt/kafka \
|
||||||
|
GLIBC_VERSION=$glibc_version
|
||||||
|
|
||||||
|
ENV PATH=${PATH}:${KAFKA_HOME}/bin
|
||||||
|
|
||||||
|
COPY ./kafka/download-kafka.sh ./kafka/start-kafka.sh ./kafka/broker-list.sh ./kafka/create-topics.sh ./kafka/versions.sh /tmp/
|
||||||
|
|
||||||
|
RUN apk add --no-cache bash curl jq docker \
|
||||||
|
&& chmod a+x /tmp/*.sh \
|
||||||
|
&& mv /tmp/start-kafka.sh /tmp/broker-list.sh /tmp/create-topics.sh /tmp/versions.sh /usr/bin \
|
||||||
|
&& sync && /tmp/download-kafka.sh \
|
||||||
|
&& tar xfz /tmp/kafka_${SCALA_VERSION}-${KAFKA_VERSION}.tgz -C /opt \
|
||||||
|
&& rm /tmp/kafka_${SCALA_VERSION}-${KAFKA_VERSION}.tgz \
|
||||||
|
&& ln -s /opt/kafka_${SCALA_VERSION}-${KAFKA_VERSION} ${KAFKA_HOME} \
|
||||||
|
&& rm /tmp/* \
|
||||||
|
&& wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-${GLIBC_VERSION}.apk \
|
||||||
|
&& apk add --no-cache --allow-untrusted glibc-${GLIBC_VERSION}.apk \
|
||||||
|
&& rm glibc-${GLIBC_VERSION}.apk
|
||||||
|
|
||||||
|
COPY ./kafka/overrides /opt/overrides
|
||||||
|
|
||||||
|
VOLUME ["/kafka"]
|
||||||
|
|
||||||
|
# Use "exec" form so that it runs as PID 1 (useful for graceful shutdown)
|
||||||
|
CMD ["start-kafka.sh"]
|
18
kafka/docker-compose-kafka.yml
Normal file
18
kafka/docker-compose-kafka.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
zookeeper:
|
||||||
|
image: wurstmeister/zookeeper
|
||||||
|
ports:
|
||||||
|
- "2181:2181"
|
||||||
|
kafka:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./Dockerfile-kafka
|
||||||
|
ports:
|
||||||
|
- "9092:9092"
|
||||||
|
environment:
|
||||||
|
KAFKA_ADVERTISED_HOST_NAME: 10.0.1.20
|
||||||
|
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
||||||
|
KAFKA_CREATE_TOPICS: "cheonan-kt:1:1,cheonan-lg:1:1"
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker_kafka.sock:/var/run/docker.sock
|
5
kafka/kafka/broker-list.sh
Normal file
5
kafka/kafka/broker-list.sh
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
CONTAINERS=$(docker ps | grep 9092 | awk '{print $1}')
|
||||||
|
BROKERS=$(for CONTAINER in ${CONTAINERS}; do docker port "$CONTAINER" 9092 | sed -e "s/0.0.0.0:/$HOST_IP:/g"; done)
|
||||||
|
echo "${BROKERS//$'\n'/,}"
|
57
kafka/kafka/create-topics.sh
Normal file
57
kafka/kafka/create-topics.sh
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ -z "$KAFKA_CREATE_TOPICS" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$START_TIMEOUT" ]]; then
|
||||||
|
START_TIMEOUT=600
|
||||||
|
fi
|
||||||
|
|
||||||
|
start_timeout_exceeded=false
|
||||||
|
count=0
|
||||||
|
step=10
|
||||||
|
while netstat -lnt | awk '$4 ~ /:'"$KAFKA_PORT"'$/ {exit 1}'; do
|
||||||
|
echo "waiting for kafka to be ready"
|
||||||
|
sleep $step;
|
||||||
|
count=$((count + step))
|
||||||
|
if [ $count -gt $START_TIMEOUT ]; then
|
||||||
|
start_timeout_exceeded=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if $start_timeout_exceeded; then
|
||||||
|
echo "Not able to auto-create topic (waited for $START_TIMEOUT sec)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# introduced in 0.10. In earlier versions, this will fail because the topic already exists.
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source "/usr/bin/versions.sh"
|
||||||
|
if [[ "$MAJOR_VERSION" == "0" && "$MINOR_VERSION" -gt "9" ]] || [[ "$MAJOR_VERSION" -gt "0" ]]; then
|
||||||
|
KAFKA_0_10_OPTS="--if-not-exists"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Expected format:
|
||||||
|
# name:partitions:replicas:cleanup.policy
|
||||||
|
IFS="${KAFKA_CREATE_TOPICS_SEPARATOR-,}"; for topicToCreate in $KAFKA_CREATE_TOPICS; do
|
||||||
|
echo "creating topics: $topicToCreate"
|
||||||
|
IFS=':' read -r -a topicConfig <<< "$topicToCreate"
|
||||||
|
config=
|
||||||
|
if [ -n "${topicConfig[3]}" ]; then
|
||||||
|
config="--config=cleanup.policy=${topicConfig[3]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
COMMAND="JMX_PORT='' ${KAFKA_HOME}/bin/kafka-topics.sh \\
|
||||||
|
--create \\
|
||||||
|
--zookeeper ${KAFKA_ZOOKEEPER_CONNECT} \\
|
||||||
|
--topic ${topicConfig[0]} \\
|
||||||
|
--partitions ${topicConfig[1]} \\
|
||||||
|
--replication-factor ${topicConfig[2]} \\
|
||||||
|
${config} \\
|
||||||
|
${KAFKA_0_10_OPTS} &"
|
||||||
|
eval "${COMMAND}"
|
||||||
|
done
|
||||||
|
|
||||||
|
wait
|
18
kafka/kafka/download-kafka.sh
Normal file
18
kafka/kafka/download-kafka.sh
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source "/usr/bin/versions.sh"
|
||||||
|
|
||||||
|
FILENAME="kafka_${SCALA_VERSION}-${KAFKA_VERSION}.tgz"
|
||||||
|
|
||||||
|
url=$(curl --stderr /dev/null "https://www.apache.org/dyn/closer.cgi?path=/kafka/${KAFKA_VERSION}/${FILENAME}&as_json=1" | jq -r '"\(.preferred)\(.path_info)"')
|
||||||
|
|
||||||
|
# Test to see if the suggested mirror has this version, currently pre 2.1.1 versions
|
||||||
|
# do not appear to be actively mirrored. This may also be useful if closer.cgi is down.
|
||||||
|
if [[ ! $(curl -f -s -r 0-1 "${url}") ]]; then
|
||||||
|
echo "Mirror does not have desired version, downloading direct from Apache"
|
||||||
|
url="https://archive.apache.org/dist/kafka/${KAFKA_VERSION}/${FILENAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Downloading Kafka from $url"
|
||||||
|
wget "${url}" -O "/tmp/${FILENAME}"
|
6
kafka/kafka/overrides/0.9.0.1.sh
Normal file
6
kafka/kafka/overrides/0.9.0.1.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
# Kafka 0.9.x.x has a 'listeners' config by default. We need to remove this
|
||||||
|
# as the user may be configuring via the host.name / advertised.host.name properties
|
||||||
|
echo "Removing 'listeners' from server.properties pre-bootstrap"
|
||||||
|
sed -i -e '/^listeners=/d' "$KAFKA_HOME/config/server.properties"
|
149
kafka/kafka/start-kafka.sh
Normal file
149
kafka/kafka/start-kafka.sh
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
# Allow specific kafka versions to perform any unique bootstrap operations
|
||||||
|
OVERRIDE_FILE="/opt/overrides/${KAFKA_VERSION}.sh"
|
||||||
|
if [[ -x "$OVERRIDE_FILE" ]]; then
|
||||||
|
echo "Executing override file $OVERRIDE_FILE"
|
||||||
|
eval "$OVERRIDE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Store original IFS config, so we can restore it at various stages
|
||||||
|
ORIG_IFS=$IFS
|
||||||
|
|
||||||
|
if [[ -z "$KAFKA_ZOOKEEPER_CONNECT" ]]; then
|
||||||
|
echo "ERROR: missing mandatory config: KAFKA_ZOOKEEPER_CONNECT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$KAFKA_PORT" ]]; then
|
||||||
|
export KAFKA_PORT=9092
|
||||||
|
fi
|
||||||
|
|
||||||
|
create-topics.sh &
|
||||||
|
unset KAFKA_CREATE_TOPICS
|
||||||
|
|
||||||
|
if [[ -z "$KAFKA_ADVERTISED_PORT" && \
|
||||||
|
-z "$KAFKA_LISTENERS" && \
|
||||||
|
-z "$KAFKA_ADVERTISED_LISTENERS" && \
|
||||||
|
-S /var/run/docker.sock ]]; then
|
||||||
|
KAFKA_ADVERTISED_PORT=$(docker port "$(hostname)" $KAFKA_PORT | sed -r 's/.*:(.*)/\1/g' | head -n1)
|
||||||
|
export KAFKA_ADVERTISED_PORT
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$KAFKA_BROKER_ID" ]]; then
|
||||||
|
if [[ -n "$BROKER_ID_COMMAND" ]]; then
|
||||||
|
KAFKA_BROKER_ID=$(eval "$BROKER_ID_COMMAND")
|
||||||
|
export KAFKA_BROKER_ID
|
||||||
|
else
|
||||||
|
# By default auto allocate broker ID
|
||||||
|
export KAFKA_BROKER_ID=-1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$KAFKA_LOG_DIRS" ]]; then
|
||||||
|
export KAFKA_LOG_DIRS="/kafka/kafka-logs-$HOSTNAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$KAFKA_HEAP_OPTS" ]]; then
|
||||||
|
sed -r -i 's/(export KAFKA_HEAP_OPTS)="(.*)"/\1="'"$KAFKA_HEAP_OPTS"'"/g' "$KAFKA_HOME/bin/kafka-server-start.sh"
|
||||||
|
unset KAFKA_HEAP_OPTS
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$HOSTNAME_COMMAND" ]]; then
|
||||||
|
HOSTNAME_VALUE=$(eval "$HOSTNAME_COMMAND")
|
||||||
|
|
||||||
|
# Replace any occurrences of _{HOSTNAME_COMMAND} with the value
|
||||||
|
IFS=$'\n'
|
||||||
|
for VAR in $(env); do
|
||||||
|
if [[ $VAR =~ ^KAFKA_ && "$VAR" =~ "_{HOSTNAME_COMMAND}" ]]; then
|
||||||
|
eval "export ${VAR//_\{HOSTNAME_COMMAND\}/$HOSTNAME_VALUE}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
IFS=$ORIG_IFS
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$PORT_COMMAND" ]]; then
|
||||||
|
PORT_VALUE=$(eval "$PORT_COMMAND")
|
||||||
|
|
||||||
|
# Replace any occurrences of _{PORT_COMMAND} with the value
|
||||||
|
IFS=$'\n'
|
||||||
|
for VAR in $(env); do
|
||||||
|
if [[ $VAR =~ ^KAFKA_ && "$VAR" =~ "_{PORT_COMMAND}" ]]; then
|
||||||
|
eval "export ${VAR//_\{PORT_COMMAND\}/$PORT_VALUE}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
IFS=$ORIG_IFS
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$RACK_COMMAND" && -z "$KAFKA_BROKER_RACK" ]]; then
|
||||||
|
KAFKA_BROKER_RACK=$(eval "$RACK_COMMAND")
|
||||||
|
export KAFKA_BROKER_RACK
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try and configure minimal settings or exit with error if there isn't enough information
|
||||||
|
if [[ -z "$KAFKA_ADVERTISED_HOST_NAME$KAFKA_LISTENERS" ]]; then
|
||||||
|
if [[ -n "$KAFKA_ADVERTISED_LISTENERS" ]]; then
|
||||||
|
echo "ERROR: Missing environment variable KAFKA_LISTENERS. Must be specified when using KAFKA_ADVERTISED_LISTENERS"
|
||||||
|
exit 1
|
||||||
|
elif [[ -z "$HOSTNAME_VALUE" ]]; then
|
||||||
|
echo "ERROR: No listener or advertised hostname configuration provided in environment."
|
||||||
|
echo " Please define KAFKA_LISTENERS / (deprecated) KAFKA_ADVERTISED_HOST_NAME"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Maintain existing behaviour
|
||||||
|
# If HOSTNAME_COMMAND is provided, set that to the advertised.host.name value if listeners are not defined.
|
||||||
|
export KAFKA_ADVERTISED_HOST_NAME="$HOSTNAME_VALUE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#Issue newline to config file in case there is not one already
|
||||||
|
echo "" >> "$KAFKA_HOME/config/server.properties"
|
||||||
|
|
||||||
|
(
|
||||||
|
function updateConfig() {
|
||||||
|
key=$1
|
||||||
|
value=$2
|
||||||
|
file=$3
|
||||||
|
|
||||||
|
# Omit $value here, in case there is sensitive information
|
||||||
|
echo "[Configuring] '$key' in '$file'"
|
||||||
|
|
||||||
|
# If config exists in file, replace it. Otherwise, append to file.
|
||||||
|
if grep -E -q "^#?$key=" "$file"; then
|
||||||
|
sed -r -i "s@^#?$key=.*@$key=$value@g" "$file" #note that no config values may contain an '@' char
|
||||||
|
else
|
||||||
|
echo "$key=$value" >> "$file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fixes #312
|
||||||
|
# KAFKA_VERSION + KAFKA_HOME + grep -rohe KAFKA[A-Z0-0_]* /opt/kafka/bin | sort | uniq | tr '\n' '|'
|
||||||
|
EXCLUSIONS="|KAFKA_VERSION|KAFKA_HOME|KAFKA_DEBUG|KAFKA_GC_LOG_OPTS|KAFKA_HEAP_OPTS|KAFKA_JMX_OPTS|KAFKA_JVM_PERFORMANCE_OPTS|KAFKA_LOG|KAFKA_OPTS|"
|
||||||
|
|
||||||
|
# Read in env as a new-line separated array. This handles the case of env variables have spaces and/or carriage returns. See #313
|
||||||
|
IFS=$'\n'
|
||||||
|
for VAR in $(env)
|
||||||
|
do
|
||||||
|
env_var=$(echo "$VAR" | cut -d= -f1)
|
||||||
|
if [[ "$EXCLUSIONS" = *"|$env_var|"* ]]; then
|
||||||
|
echo "Excluding $env_var from broker config"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $env_var =~ ^KAFKA_ ]]; then
|
||||||
|
kafka_name=$(echo "$env_var" | cut -d_ -f2- | tr '[:upper:]' '[:lower:]' | tr _ .)
|
||||||
|
updateConfig "$kafka_name" "${!env_var}" "$KAFKA_HOME/config/server.properties"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $env_var =~ ^LOG4J_ ]]; then
|
||||||
|
log4j_name=$(echo "$env_var" | tr '[:upper:]' '[:lower:]' | tr _ .)
|
||||||
|
updateConfig "$log4j_name" "${!env_var}" "$KAFKA_HOME/config/log4j.properties"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ -n "$CUSTOM_INIT_SCRIPT" ]] ; then
|
||||||
|
eval "$CUSTOM_INIT_SCRIPT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$KAFKA_HOME/bin/kafka-server-start.sh" "$KAFKA_HOME/config/server.properties"
|
7
kafka/kafka/versions.sh
Normal file
7
kafka/kafka/versions.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
MAJOR_VERSION=$(echo "$KAFKA_VERSION" | cut -d. -f1)
|
||||||
|
export MAJOR_VERSION
|
||||||
|
|
||||||
|
MINOR_VERSION=$(echo "$KAFKA_VERSION" | cut -d. -f2)
|
||||||
|
export MINOR_VERSION
|
12
kafka/restart-kafka.sh
Normal file
12
kafka/restart-kafka.sh
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
docker-compose -f /data/cheonan_ep/docker/col_server/kafka/docker-compose-kafka.yml down
|
||||||
|
docker-compose -f /data/cheonan_ep/docker/col_server/kafka/docker-compose-kafka.yml up -d
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
docker-compose -f /data/cheonan_ep/docker/col_server/kafka/docker-compose-kafka.yml ps
|
||||||
|
|
10
logstash/Dockerfile-logstash
Normal file
10
logstash/Dockerfile-logstash
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
FROM docker.elastic.co/logstash/logstash-oss:7.15.2
|
||||||
|
|
||||||
|
ENV TZ=Asia/Seoul
|
||||||
|
|
||||||
|
COPY lib_logstash/postgresql-42.2.24.jar /usr/share/logstash/postgresql-42.2.24.jar
|
||||||
|
|
||||||
|
RUN rm -f /usr/share/logstash/pipeline/logstash_kt.conf && \
|
||||||
|
rm -f /usr/share/logstash/pipeline/logstash_lg.conf && \
|
||||||
|
bin/logstash-plugin install logstash-output-jdbc
|
49
logstash/conf/logstash_iot/logstash_kt.conf
Normal file
49
logstash/conf/logstash_iot/logstash_kt.conf
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
input {
|
||||||
|
kafka {
|
||||||
|
bootstrap_servers => "10.0.1.10:9092"
|
||||||
|
group_id => "cheonan"
|
||||||
|
topics => ["cheonan-kt"]
|
||||||
|
codec => json {
|
||||||
|
charset=>"UTF-8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# filter {
|
||||||
|
# mutate {
|
||||||
|
# split => {"message" => " "}
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
output {
|
||||||
|
# stdout {
|
||||||
|
# codec => rubydebug
|
||||||
|
# }
|
||||||
|
# stdout { codec => line { format => 'tttttttttttttttttttttttt' } }
|
||||||
|
if [data_type] == "cheonan_kt" {
|
||||||
|
jdbc {
|
||||||
|
connection_string => "jdbc:postgresql://pg-219s5.vpc-cdb-kr.gov-ntruss.com:5432/cheonan_ep?user=cheonan&password=cjsdkstl2023!#"
|
||||||
|
driver_jar_path => "/usr/share/logstash/postgresql-42.2.24.jar"
|
||||||
|
max_pool_size => 2
|
||||||
|
connection_timeout => 25000
|
||||||
|
statement => [
|
||||||
|
"INSERT INTO energy.raw_data_kt (
|
||||||
|
tm,
|
||||||
|
request_type,
|
||||||
|
client_host,
|
||||||
|
body
|
||||||
|
) VALUES (
|
||||||
|
now(),
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?
|
||||||
|
)",
|
||||||
|
"[cheonan_kt][request_type]",
|
||||||
|
"[cheonan_kt][client_host]",
|
||||||
|
"[cheonan_kt][body]"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
49
logstash/conf/logstash_iot/logstash_lg.conf
Normal file
49
logstash/conf/logstash_iot/logstash_lg.conf
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
input {
|
||||||
|
kafka {
|
||||||
|
bootstrap_servers => "10.0.1.10:9092"
|
||||||
|
group_id => "cheonan"
|
||||||
|
topics => ["cheonan-lg"]
|
||||||
|
codec => json {
|
||||||
|
charset=>"UTF-8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# filter {
|
||||||
|
# mutate {
|
||||||
|
# split => {"message" => " "}
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
output {
|
||||||
|
# stdout {
|
||||||
|
# codec => rubydebug
|
||||||
|
# }
|
||||||
|
# stdout { codec => line { format => 'tttttttttttttttttttttttt' } }
|
||||||
|
if [data_type] == "cheonan_lg" {
|
||||||
|
jdbc {
|
||||||
|
connection_string => "jdbc:postgresql://pg-219s5.vpc-cdb-kr.gov-ntruss.com:5432/cheonan_ep?user=cheonan&password=cjsdkstl2023!#"
|
||||||
|
driver_jar_path => "/usr/share/logstash/postgresql-42.2.24.jar"
|
||||||
|
max_pool_size => 2
|
||||||
|
connection_timeout => 25000
|
||||||
|
statement => [
|
||||||
|
"INSERT INTO energy.raw_data_lg (
|
||||||
|
tm,
|
||||||
|
request_type,
|
||||||
|
client_host,
|
||||||
|
body
|
||||||
|
) VALUES (
|
||||||
|
now(),
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?
|
||||||
|
)",
|
||||||
|
"[cheonan_lg][request_type]",
|
||||||
|
"[cheonan_lg][client_host]",
|
||||||
|
"[cheonan_lg][body]"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
15
logstash/docker-compose-logstash.yml
Normal file
15
logstash/docker-compose-logstash.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
logstash-herit:
|
||||||
|
container_name: logstash
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./Dockerfile-logstash
|
||||||
|
image: logstash:latest
|
||||||
|
volumes:
|
||||||
|
- /data/cheonan_ep/docker/col_server/logstash/conf/logstash_iot:/usr/share/logstash/pipeline
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-file: "5"
|
||||||
|
max-size: "100m"
|
BIN
logstash/lib_logstash/postgresql-42.2.12.jar
Normal file
BIN
logstash/lib_logstash/postgresql-42.2.12.jar
Normal file
Binary file not shown.
BIN
logstash/lib_logstash/postgresql-42.2.24.jar
Normal file
BIN
logstash/lib_logstash/postgresql-42.2.24.jar
Normal file
Binary file not shown.
12
logstash/restart-logstash.sh
Normal file
12
logstash/restart-logstash.sh
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
docker-compose -f /data/cheonan_ep/docker/col_server/logstash/docker-compose-logstash.yml down
|
||||||
|
docker-compose -f /data/cheonan_ep/docker/col_server/logstash/docker-compose-logstash.yml up -d
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
docker-compose -f /data/cheonan_ep/docker/col_server/logstash/docker-compose-logstash.yml ps
|
||||||
|
|
234
organize_image_files/organize_image_files.py
Normal file
234
organize_image_files/organize_image_files.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
import os
|
||||||
|
import stat
|
||||||
|
import shutil
|
||||||
|
import datetime
|
||||||
|
import psycopg2 # PostgreSQL 데이터베이스 연결 라이브러리
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Custom handler class definition
|
||||||
|
class CustomTimedRotatingFileHandler(TimedRotatingFileHandler):
|
||||||
|
def __init__(self, base_log_path, when="midnight", interval=1, backupCount=7):
|
||||||
|
self.base_log_path = base_log_path
|
||||||
|
# Initialize log file path explicitly during instantiation
|
||||||
|
self.update_log_path()
|
||||||
|
super().__init__(self.baseFilename, when=when, interval=interval, backupCount=backupCount)
|
||||||
|
|
||||||
|
def update_log_path(self):
|
||||||
|
current_time = datetime.datetime.now()
|
||||||
|
log_directory = os.path.join(
|
||||||
|
self.base_log_path,
|
||||||
|
current_time.strftime("%Y"),
|
||||||
|
current_time.strftime("%m")
|
||||||
|
)
|
||||||
|
log_filename = current_time.strftime("%d_allscore.log")
|
||||||
|
|
||||||
|
# Set log file path
|
||||||
|
self.baseFilename = os.path.join(log_directory, log_filename)
|
||||||
|
|
||||||
|
# Create directory if not exists
|
||||||
|
if not os.path.exists(log_directory):
|
||||||
|
os.makedirs(log_directory)
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
# Update the log path at each log entry point
|
||||||
|
self.update_log_path()
|
||||||
|
super().emit(record)
|
||||||
|
|
||||||
|
# Logger configuration
|
||||||
|
logger = logging.getLogger("allscore_logger")
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# Stream handler for stdout
|
||||||
|
stream_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
stream_handler.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# Log format configuration
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
stream_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# Custom file handler configuration
|
||||||
|
base_log_path = "/home/ec2-user/eld/card_service/backend/organize_image_files/logs"
|
||||||
|
file_handler = CustomTimedRotatingFileHandler(base_log_path=base_log_path, when="midnight", interval=1, backupCount=7)
|
||||||
|
file_handler.setLevel(logging.DEBUG)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# Add handlers to the logger
|
||||||
|
logger.addHandler(stream_handler)
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
|
||||||
|
# 파일명을 갖고 뉴스룸 SEQ찾기
|
||||||
|
def get_user_seq(file_name):
|
||||||
|
try:
|
||||||
|
# PostgreSQL connection configuration
|
||||||
|
conn = psycopg2.connect(
|
||||||
|
dbname='allscore',
|
||||||
|
user='eldsoft',
|
||||||
|
password='eld240510',
|
||||||
|
host='3.34.123.25', # e.g., 'localhost' or IP address
|
||||||
|
port='8088' # e.g., '5432'
|
||||||
|
)
|
||||||
|
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# Execute SQL query
|
||||||
|
query = f"""
|
||||||
|
select user_seq
|
||||||
|
from manage_user
|
||||||
|
where profile_img like '%{file_name}%'
|
||||||
|
"""
|
||||||
|
cur.execute(query)
|
||||||
|
result = cur.fetchall()
|
||||||
|
|
||||||
|
# Close the cursor and the connection
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"get_user_seq(파일명을 갖고 user_seq 찾기) error: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
# 서버 임시 디렉토리에서 하루전 파일들 목록 가져오기
|
||||||
|
def get_file_name():
|
||||||
|
try:
|
||||||
|
directory_path = f"/home/ec2-user/eld/card_service/backend/fastapi/app/images/user/temp_dir/profile_img/"
|
||||||
|
file_list = os.listdir(directory_path)
|
||||||
|
file_list = [f for f in file_list if os.path.isfile(os.path.join(directory_path, f))]
|
||||||
|
target_file_list = []
|
||||||
|
for file_name in file_list:
|
||||||
|
if extract_datetime_from_filename(file_name):
|
||||||
|
target_file_list.append(file_name)
|
||||||
|
return file_list
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"임시 이미지 디렉토리에서 이미지 파일 가져오기 실패\nerror msg: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def extract_datetime_from_filename(filename):
|
||||||
|
# 정규 표현식으로 파일명에서 날짜 부분 추출 (확장자는 .jpg, .jpeg, .png, .bmp만 허용)
|
||||||
|
match = re.search(r'IMG_\d+_(\d+)\.(jpg|jpeg|png|bmp)', filename, re.IGNORECASE)
|
||||||
|
target_dt = match.group(1) # 추출된 날짜 부분 반환
|
||||||
|
reference_date = get_yesterday_time_str()
|
||||||
|
if target_dt < reference_date:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 전날 00시 이전 파일명 갖고 오기위한 시간 str얻는 함수
|
||||||
|
def get_yesterday_time_str():
|
||||||
|
# 어제 시간 가져오기
|
||||||
|
now = datetime.datetime.now() - datetime.timedelta(days=1)
|
||||||
|
# YYYYMMDDHHMMSSsss 형식으로 포맷
|
||||||
|
formatted_time = now.strftime('%Y%m%d%H%M%S%f')[:17]
|
||||||
|
return formatted_time[:8] + '000000000'
|
||||||
|
|
||||||
|
|
||||||
|
# 파일 이동
|
||||||
|
def move_file(user_seq, file_name):
|
||||||
|
try:
|
||||||
|
source_path = f"/home/ec2-user/eld/card_service/backend/fastapi/app/images/user/temp_dir/profile_img/{file_name}"
|
||||||
|
destination_dir = f"/home/ec2-user/eld/card_service/backend/fastapi/app/images/user/{user_seq}/profile_img/"
|
||||||
|
|
||||||
|
# 대상 디렉토리가 존재하지 않으면 생성
|
||||||
|
if not os.path.exists(destination_dir):
|
||||||
|
os.makedirs(destination_dir, mode=0o775)
|
||||||
|
|
||||||
|
# 파일 쓰기 권한 부여
|
||||||
|
os.chmod(source_path, stat.S_IWUSR)
|
||||||
|
|
||||||
|
# 파일 이동
|
||||||
|
shutil.move(source_path, destination_dir)
|
||||||
|
logger.debug(f"파일이 성공적으로 {destination_dir}로 이동되었습니다.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"파일 이동 중 오류가 발생했습니다: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
# 파일 삭제
|
||||||
|
def delete_file(file_name):
|
||||||
|
try:
|
||||||
|
file_path = f"/home/ec2-user/eld/card_service/backend/fastapi/app/images/user/temp_dir/profile_img/{file_name}"
|
||||||
|
# 파일이 존재하는지 확인 후 삭제
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
# 파일 쓰기 권한 부여
|
||||||
|
os.chmod(file_path, stat.S_IWUSR)
|
||||||
|
# 파일 삭제
|
||||||
|
os.remove(file_path)
|
||||||
|
logger.debug(f"{file_path} 파일이 성공적으로 삭제되었습니다.")
|
||||||
|
else:
|
||||||
|
logger.debug(f"{file_path} 파일이 존재하지 않습니다.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"파일 삭제 중 오류가 발생했습니다: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
# 이동된 파일명 맞게 DB 수정하기
|
||||||
|
def update_new_dir(user_seq, file_name):
|
||||||
|
try:
|
||||||
|
# PostgreSQL connection configuration
|
||||||
|
conn = psycopg2.connect(
|
||||||
|
dbname='allscore',
|
||||||
|
user='eldsoft',
|
||||||
|
password='eld240510',
|
||||||
|
host='3.34.123.25', # e.g., 'localhost' or IP address
|
||||||
|
port='8088' # e.g., '5432'
|
||||||
|
)
|
||||||
|
|
||||||
|
cur = conn.cursor()
|
||||||
|
logger.info("Connected to the database successfully.")
|
||||||
|
|
||||||
|
source_path = f"/user/temp_dir/profile_img/{file_name}"
|
||||||
|
destination_dir = f"/user/{user_seq}/profile_img/{file_name}"
|
||||||
|
|
||||||
|
# Execute SQL query
|
||||||
|
query = f"""
|
||||||
|
update manage_user
|
||||||
|
set
|
||||||
|
profile_img = '{destination_dir}'
|
||||||
|
where user_seq = {user_seq}
|
||||||
|
"""
|
||||||
|
logger.debug(f"query: {query}")
|
||||||
|
cur.execute(query)
|
||||||
|
|
||||||
|
# Commit the transaction
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
# Close the cursor and the connection
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"update_new_dir(이동된 파일명 맞게 DB 수정하기) error: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
# 회원 프로필이미지 임시 이미지 파일 정리 수행
|
||||||
|
def manage_profile_img():
|
||||||
|
# 임시 디렉토리에서 어제보다 전에 만들어진 파일명을 다 가져오기
|
||||||
|
target_file_name = get_file_name()
|
||||||
|
logger.info(f"대상 파일 개수: {len(target_file_name)}개")
|
||||||
|
# 각 파일명별로 for문 수행
|
||||||
|
for file_name in target_file_name:
|
||||||
|
# 해당 파일명이 포함된 게시글의 seq찾기
|
||||||
|
user_seq = get_user_seq(file_name)
|
||||||
|
user_seq = '0' if len(user_seq) == 0 else user_seq[0][0]
|
||||||
|
logger.info(f"seq: {user_seq}")
|
||||||
|
# 해당 seq에 맞게 파일을 이동시키기, seq가 0인건(DB조회 되지 않은건 0으로 처리했기에) 삭제
|
||||||
|
if user_seq == '0':
|
||||||
|
# seq가 0인건(DB조회 되지 않은건 0으로 처리했기에) 삭제
|
||||||
|
delete_file(user_seq, file_name)
|
||||||
|
else:
|
||||||
|
# 해당 seq에 맞게 파일을 이동시키기
|
||||||
|
move_file(user_seq, file_name)
|
||||||
|
# 이동시킨 디렉토리명에 맞춰서 DB내용(src)도 수정해주기
|
||||||
|
update_new_dir(user_seq, file_name)
|
||||||
|
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
if __name__ == "__main__":
|
||||||
|
manage_profile_img()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user