첫커밋
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