Skip to content

Commit 01d7bbf

Browse files
authored
Merge pull request #44 from ResQ-404Found/feat/#43
[Notification] 재난 알림 발송 방식 변경 및 알림 설정(필터링) 구현
2 parents a9d1a8e + 0c1f5e5 commit 01d7bbf

21 files changed

Lines changed: 403 additions & 122 deletions

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ COPY . /app/
66

77
RUN pip install --upgrade pip && \
88
pip install "fastapi[standard]" \
9-
sqlmodel pymysql redis passlib[bcrypt] PyJWT requests pandas boto3 aiosmtplib cryptography apscheduler openai langchain langchain-openai celery firebase_admin
9+
sqlmodel pymysql redis passlib[bcrypt] PyJWT requests pandas boto3 aiosmtplib cryptography apscheduler openai langchain langchain-openai firebase_admin
1010

1111
RUN pip install cryptography
1212

README.md

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ pip install apscheduler
2525
pip install openai
2626
pip install langchain
2727
pip install langchain-openai
28-
pip install celery
2928
pip install firebase_admin
3029
```
3130

@@ -81,58 +80,68 @@ project-root/
8180
8281
├── app/
8382
│ ├── core/
83+
│ │ ├── firebase.py
8484
│ │ └── redis.py
8585
│ ├── db/
8686
│ │ ├── session.py
8787
│ │ └── init_db.py
8888
│ ├── handlers/
89+
│ │ ├── chatbot_handler.py
8990
│ │ ├── comment_handler.py
9091
│ │ ├── disaster_handler.py
9192
│ │ ├── email_handler.py
93+
│ │ ├── fcm_handler.py
9294
│ │ ├── like_handler.py
95+
│ │ ├── notification_disastertype_handler.py
96+
│ │ ├── notification_handler.py
97+
│ │ ├── notification_region_handler.py
9398
│ │ ├── post_handler.py
9499
│ │ ├── shelter_handler.py
95100
│ │ └── user_handler.py
96101
│ ├── models/
102+
│ │ ├── chatbot_model.py
97103
│ │ ├── comment_model.py
98104
│ │ ├── disaster_model.py
99-
│ │ ├── region_model.py
100105
│ │ ├── disaster_region_model.py
101106
│ │ ├── like_model.py
107+
│ │ ├── notification_model.py
102108
│ │ ├── post_model.py
109+
│ │ ├── region_model.py
103110
│ │ ├── shelter_model.py
104-
│ │ ├── chatbot_model.py
105-
│ │ ├── notification_model.py
106111
│ │ └── user_model.py
107112
│ ├── schemas/
113+
│ │ ├── chatbot_schema.py
108114
│ │ ├── comment_schema.py
109115
│ │ ├── common_schema.py
110116
│ │ ├── disaster_schema.py
111117
│ │ ├── email_schema.py
112118
│ │ ├── like_schema.py
119+
│ │ ├── notification_disastertype_schema.py
120+
│ │ ├── notification_region_schema.py
121+
│ │ ├── notification_schema.py
113122
│ │ ├── post_schema.py
114123
│ │ ├── shelter_schema.py
115-
│ │ ├── chatbot_schema.py
116124
│ │ └── user_schema.py
117125
│ ├── services/
126+
│ │ ├── chatbot_service.py
118127
│ │ ├── comment_service.py
119128
│ │ ├── disaster_region_service.py
120129
│ │ ├── disaster_service.py
121130
│ │ ├── email_service.py
131+
│ │ ├── fcm_service.py
122132
│ │ ├── like_service.py
133+
│ │ ├── notification_disastertype_service.py
134+
│ │ ├── notification_region_service.py
135+
│ │ ├── notification_service.py
123136
│ │ ├── post_service.py
124137
│ │ ├── region_service.py
125138
│ │ ├── shelter_service.py
126-
│ │ ├── chatbot_service.py
127-
│ │ ├── notification_service.py
128-
│ │ └── user_service.py
129-
│ ├── tasks/
130-
│ │ └── fcm_task.py
139+
│ │ └── user_service.py
131140
│ └── utils/
132141
│ ├── fcm_util.py
133142
│ ├── jwt_util.py
134-
│ ├── s3_util.py
135-
│ └── redis_util.py
143+
│ ├── redis_util.py
144+
│ └── s3_util.py
136145
```
137146

138147
### 개발 버전 관리

app/core/firebase.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import firebase_admin
2+
from firebase_admin import credentials
3+
import os
4+
import json
5+
6+
def init_firebase():
7+
with open("secrets/firebase_service_account.json") as f:
8+
cred_dict = json.load(f)
9+
cred = credentials.Certificate(cred_dict)
10+
firebase_admin.initialize_app(cred)
11+
print("[INFO] Firebase Admin SDK Initialized!")

app/db/init_db.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from sqlmodel import SQLModel, Session
22
from app.db.session import db_engine
3+
from app.models.notification_model import NotificationDisasterType, NotificationRegion
4+
from app.models.region_model import Region
35
from app.models.user_model import User
46
from app.schemas.user_schema import UserCreate
57
from app.services.user_service import UserService
68
from sqlalchemy import text
7-
# from app.services.user_service import pwd_context
9+
from app.services.user_service import pwd_context
810

911
async def create_db_and_tables():
1012
# DB 리셋 함수, 필요에 따라 주석 해제
@@ -17,7 +19,7 @@ async def create_db_and_tables():
1719
SQLModel.metadata.create_all(db_engine)
1820

1921

20-
# # 3. 테스트 유저 삽입
22+
# 3. 테스트 유저 삽입
2123
# with Session(db_engine) as session:
2224
# dummy_user = User(
2325
# login_id="test",
@@ -29,4 +31,31 @@ async def create_db_and_tables():
2931
# status="active"
3032
# )
3133
# session.add(dummy_user)
32-
# session.commit()
34+
# session.commit()
35+
# session.refresh(dummy_user)
36+
37+
# region = Region(
38+
# sido="경상북도",
39+
# sigungu="의성군",
40+
# )
41+
# session.add(region)
42+
# session.commit()
43+
# session.refresh(region)
44+
45+
# user_id = dummy_user.id
46+
# region_id = region.id
47+
48+
# with Session(db_engine) as session:
49+
# notification_region = NotificationRegion(
50+
# user_id=user_id,
51+
# region_id=region_id
52+
# )
53+
# session.add(notification_region)
54+
# session.commit()
55+
56+
# notification_disastertype = NotificationDisasterType(
57+
# user_id=user_id,
58+
# disaster_type="폭염" # 예시: 원하는 재난 타입
59+
# )
60+
# session.add(notification_disastertype)
61+
# session.commit()

app/handlers/fcm_handler.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from fastapi import APIRouter, Depends
2+
3+
from app.handlers.user_handler import get_current_user
4+
from app.models.user_model import User
5+
from app.schemas.common_schema import ApiResponse
6+
from app.schemas.user_schema import FCMTokenUpdate
7+
from app.services.fcm_service import FcmService
8+
9+
router = APIRouter()
10+
11+
@router.patch("/users/fcm-token", response_model_exclude_none=True)
12+
def update_fcm_token(
13+
req: FCMTokenUpdate,
14+
user: User=Depends(get_current_user),
15+
fcm_service: FcmService = Depends()
16+
) -> ApiResponse[None]:
17+
fcm_service.update_user_fcm_token(user,req.fcm_token)
18+
return ApiResponse(message="FCM 토큰이 성공적으로 등록되었습니다.")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from fastapi import APIRouter, Depends, HTTPException
2+
from sqlmodel import Session
3+
4+
from app.db.session import get_db_session
5+
from app.handlers.user_handler import get_current_user
6+
from app.models.user_model import User
7+
from app.schemas.notification_disastertype_schema import NotificationDisasterTypeCreate, NotificationDisasterTypeRead
8+
from app.services.notification_disastertype_service import (
9+
create_notification_disastertype,
10+
get_notification_disastertypes_by_user,
11+
delete_notification_disastertype
12+
)
13+
14+
router = APIRouter(prefix="/notification-disastertypes")
15+
16+
@router.post("/", response_model=NotificationDisasterTypeRead)
17+
def create_disastertype(req: NotificationDisasterTypeCreate, user: User = Depends(get_current_user), session: Session = Depends(get_db_session)):
18+
return create_notification_disastertype(session, user.id, req.disaster_type)
19+
20+
@router.get("/", response_model=list[NotificationDisasterTypeRead])
21+
def get_user_disastertypes(user: User = Depends(get_current_user), session: Session = Depends(get_db_session)):
22+
return get_notification_disastertypes_by_user(session, user.id)
23+
24+
@router.delete("/{disastertype_id}")
25+
def delete_disastertype(disastertype_id: int, user: User = Depends(get_current_user), session: Session = Depends(get_db_session)):
26+
success = delete_notification_disastertype(session, disastertype_id, user.id)
27+
if not success:
28+
raise HTTPException(status_code=404, detail="NotificationDisasterType not found")
29+
return {"message": "Deleted successfully"}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from fastapi import APIRouter, Depends
2+
from sqlmodel import Session
3+
from app.db.session import get_db_session
4+
from app.handlers.user_handler import get_current_user
5+
from app.models.user_model import User
6+
from app.schemas.notification_schema import NotificationCreate, NotificationRead
7+
from app.services.notification_service import (
8+
create_notification,
9+
get_notifications_by_user
10+
)
11+
12+
router = APIRouter(prefix="/notifications")
13+
14+
@router.post("/", response_model=NotificationRead)
15+
def create(req: NotificationCreate, user: User = Depends(get_current_user), session: Session = Depends(get_db_session)):
16+
return create_notification(session, user.id, req.disaster_id, req.title, req.body)
17+
18+
@router.get("/", response_model=list[NotificationRead])
19+
def get_user_notifications(user: User = Depends(get_current_user), session: Session = Depends(get_db_session)):
20+
return get_notifications_by_user(session, user.id)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from fastapi import APIRouter, Depends, HTTPException
2+
from sqlmodel import Session
3+
4+
from app.db.session import get_db_session
5+
from app.schemas.notification_region_schema import NotificationRegionCreate, NotificationRegionRead
6+
from app.services.notification_region_service import (
7+
create_notification_region,
8+
get_notification_regions_by_user,
9+
delete_notification_region
10+
)
11+
from app.handlers.user_handler import get_current_user
12+
from app.models.user_model import User
13+
14+
router = APIRouter(prefix="/notification-regions")
15+
16+
@router.post("/", response_model=NotificationRegionRead)
17+
def create_region_subscription(
18+
req: NotificationRegionCreate,
19+
session: Session = Depends(get_db_session),
20+
user: User = Depends(get_current_user)
21+
):
22+
return create_notification_region(session, user_id=user.id, region_id=req.region_id)
23+
24+
@router.get("/", response_model=list[NotificationRegionRead])
25+
def get_my_regions(
26+
session: Session = Depends(get_db_session),
27+
user: User = Depends(get_current_user)
28+
):
29+
return get_notification_regions_by_user(session, user.id)
30+
31+
@router.delete("/{notification_region_id}")
32+
def delete_region_subscription(
33+
notification_region_id: int,
34+
session: Session = Depends(get_db_session),
35+
user: User = Depends(get_current_user)
36+
):
37+
success = delete_notification_region(session, notification_region_id, user_id=user.id)
38+
if not success:
39+
raise HTTPException(status_code=404, detail="NotificationRegion not found or unauthorized")
40+
return {"message": "Deleted successfully"}

app/handlers/user_handler.py

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
from fastapi import APIRouter, Depends, HTTPException, Header
2-
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer, OAuth2PasswordBearer
3-
from sqlmodel import Session
4-
from app.db.session import get_db_session
2+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
53
from app.models.user_model import User
6-
from app.schemas.common_schema import ApiResponse
7-
from app.schemas.user_schema import FCMTokenUpdate, UserCreate, UserCreateResponse, UserDeleteResponse, UserLogin, UserLoginResponse, UserReadResponse, UserUpdate, UserUpdateResponse
4+
from app.schemas.user_schema import UserCreate, UserCreateResponse, UserDeleteResponse, UserLogin, UserLoginResponse, UserReadResponse, UserUpdate, UserUpdateResponse
85
from app.services.user_service import UserService
96
from app.core.redis import get_redis
107
from redis.asyncio import Redis
@@ -77,16 +74,4 @@ def get_user_info(
7774
user_service: UserService = Depends(),
7875
) -> UserReadResponse:
7976
user_data = user_service.get_info(user.id)
80-
return UserReadResponse(message="회원정보 조회 성공", data=user_data)
81-
82-
@router.patch("/users/fcm-token", response_model_exclude_none=True)
83-
def update_fcm_token(
84-
req: FCMTokenUpdate,
85-
user: User=Depends(get_current_user),
86-
user_service: UserService = Depends()
87-
) -> ApiResponse[None]:
88-
try:
89-
user_service.update_user_fcm_token(user,req.fcm_token)
90-
except ValueError as ve:
91-
raise HTTPException(status_code=400, detail=str(ve))
92-
return ApiResponse(message="FCM 토큰이 성공적으로 등록되었습니다.")
77+
return UserReadResponse(message="회원정보 조회 성공", data=user_data)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from pydantic import BaseModel
2+
3+
class NotificationDisasterTypeCreate(BaseModel):
4+
disaster_type: str
5+
6+
class NotificationDisasterTypeRead(BaseModel):
7+
id: int
8+
user_id: int
9+
disaster_type: str
10+
11+
class Config:
12+
orm_mode = True

0 commit comments

Comments
 (0)