Hp137 commited on
Commit
d7da01b
·
1 Parent(s): 354e320

fix:updated secrets

Browse files
alembic/versions/a34f13019598_drop_platform_model.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """drop platform model
2
+
3
+ Revision ID: a34f13019598
4
+ Revises: 3380d93055a7
5
+ Create Date: 2025-12-07 14:22:13.166768
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ import sqlmodel.sql.sqltypes
13
+
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision: str = 'a34f13019598'
17
+ down_revision: Union[str, Sequence[str], None] = '3380d93055a7'
18
+ branch_labels: Union[str, Sequence[str], None] = None
19
+ depends_on: Union[str, Sequence[str], None] = None
20
+
21
+
22
+ def upgrade() -> None:
23
+ """Upgrade schema."""
24
+ # ### commands auto generated by Alembic - please adjust! ###
25
+ op.drop_column('user_devices', 'platform')
26
+ op.drop_column('user_devices', 'device_model')
27
+ # ### end Alembic commands ###
28
+
29
+
30
+ def downgrade() -> None:
31
+ """Downgrade schema."""
32
+ # ### commands auto generated by Alembic - please adjust! ###
33
+ op.add_column('user_devices', sa.Column('device_model', sa.VARCHAR(), autoincrement=False, nullable=False))
34
+ op.add_column('user_devices', sa.Column('platform', sa.VARCHAR(), autoincrement=False, nullable=False))
35
+ # ### end Alembic commands ###
src/notifications/schemas.py CHANGED
@@ -1,6 +1,4 @@
1
  from pydantic import BaseModel
2
 
3
  class RegisterDeviceRequest(BaseModel):
4
- device_token: str
5
- platform: str
6
- device_model: str
 
1
  from pydantic import BaseModel
2
 
3
  class RegisterDeviceRequest(BaseModel):
4
+ device_token: str
 
 
src/notifications/service.py CHANGED
@@ -18,8 +18,6 @@ async def register_device(
18
  device = result.scalar_one_or_none()
19
 
20
  if device:
21
- device.platform = body.platform
22
- device.device_model = body.device_model
23
  device.last_seen = datetime.utcnow()
24
  device.updated_at = datetime.utcnow()
25
 
@@ -31,8 +29,6 @@ async def register_device(
31
  new_device = UserDevices(
32
  user_id=user_id,
33
  device_token=body.device_token,
34
- platform=body.platform,
35
- device_model=body.device_model,
36
  )
37
 
38
  session.add(new_device)
@@ -40,9 +36,11 @@ async def register_device(
40
  await session.refresh(new_device)
41
  return new_device
42
 
 
43
  from sqlalchemy import select
44
  from src.profile.models import UserDevices
45
 
 
46
  async def get_user_device_tokens(session, user_id):
47
  stmt = select(UserDevices.device_token).where(UserDevices.user_id == user_id)
48
  rows = (await session.execute(stmt)).all()
 
18
  device = result.scalar_one_or_none()
19
 
20
  if device:
 
 
21
  device.last_seen = datetime.utcnow()
22
  device.updated_at = datetime.utcnow()
23
 
 
29
  new_device = UserDevices(
30
  user_id=user_id,
31
  device_token=body.device_token,
 
 
32
  )
33
 
34
  session.add(new_device)
 
36
  await session.refresh(new_device)
37
  return new_device
38
 
39
+
40
  from sqlalchemy import select
41
  from src.profile.models import UserDevices
42
 
43
+
44
  async def get_user_device_tokens(session, user_id):
45
  stmt = select(UserDevices.device_token).where(UserDevices.user_id == user_id)
46
  rows = (await session.execute(stmt)).all()
src/payslip/router.py CHANGED
@@ -18,6 +18,8 @@ from src.payslip.googleservice import (
18
  )
19
  from src.payslip.utils import get_current_user_model
20
  from src.payslip.models import PayslipRequest, PayslipStatus
 
 
21
 
22
  router = APIRouter(prefix="/payslips", tags=["Payslips & Gmail"])
23
 
@@ -81,13 +83,13 @@ async def gmail_callback(
81
  existing = (await session.execute(q)).scalar_one_or_none()
82
 
83
  if existing:
84
- existing.refresh_token = refresh_token
85
  session.add(existing)
86
  else:
87
  session.add(
88
  PayslipRequest(
89
  user_id=user_id,
90
- refresh_token=refresh_token,
91
  status=PayslipStatus.PENDING,
92
  )
93
  )
 
18
  )
19
  from src.payslip.utils import get_current_user_model
20
  from src.payslip.models import PayslipRequest, PayslipStatus
21
+ from src.payslip.utils import encrypt_token
22
+
23
 
24
  router = APIRouter(prefix="/payslips", tags=["Payslips & Gmail"])
25
 
 
83
  existing = (await session.execute(q)).scalar_one_or_none()
84
 
85
  if existing:
86
+ existing.refresh_token = encrypt_token(refresh_token)
87
  session.add(existing)
88
  else:
89
  session.add(
90
  PayslipRequest(
91
  user_id=user_id,
92
+ refresh_token=encrypt_token(refresh_token),
93
  status=PayslipStatus.PENDING,
94
  )
95
  )
src/payslip/service.py CHANGED
@@ -15,6 +15,9 @@ from src.payslip.googleservice import (
15
  send_gmail,
16
  )
17
 
 
 
 
18
 
19
  async def user_team_name(session: AsyncSession, user_id):
20
  """Return user's team name."""
@@ -95,7 +98,7 @@ async def process_payslip_request(
95
  # 4. Get refresh_token from latest payslip row (DB)
96
  latest = await get_latest_payslip_row(session, user.id)
97
 
98
- refresh_token = latest.refresh_token if latest else None
99
 
100
  if not refresh_token:
101
  # No token stored yet
@@ -134,7 +137,7 @@ async def process_payslip_request(
134
  latest.status = PayslipStatus.SENT
135
  latest.requested_at = now
136
  latest.error_message = None
137
- latest.refresh_token = refresh_token # keep token
138
  session.add(latest)
139
  await session.commit()
140
  await session.refresh(latest)
 
15
  send_gmail,
16
  )
17
 
18
+ from src.payslip.utils import decrypt_token
19
+ from src.payslip.utils import encrypt_token
20
+
21
 
22
  async def user_team_name(session: AsyncSession, user_id):
23
  """Return user's team name."""
 
98
  # 4. Get refresh_token from latest payslip row (DB)
99
  latest = await get_latest_payslip_row(session, user.id)
100
 
101
+ refresh_token = decrypt_token(latest.refresh_token) if latest else None
102
 
103
  if not refresh_token:
104
  # No token stored yet
 
137
  latest.status = PayslipStatus.SENT
138
  latest.requested_at = now
139
  latest.error_message = None
140
+ latest.refresh_token = encrypt_token(refresh_token) # keep token
141
  session.add(latest)
142
  await session.commit()
143
  await session.refresh(latest)
src/payslip/utils.py CHANGED
@@ -13,6 +13,23 @@ from src.core.database import get_async_session
13
  from src.core.models import Users
14
  from src.core.config import settings
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  bearer_scheme = HTTPBearer()
17
 
18
  SECRET_KEY = settings.SECRET_KEY
 
13
  from src.core.models import Users
14
  from src.core.config import settings
15
 
16
+
17
+ from cryptography.fernet import Fernet
18
+ from src.core.config import settings
19
+
20
+
21
+ fernet = Fernet(settings.FERNET_KEY.encode())
22
+
23
+
24
+ def encrypt_token(token: str) -> str:
25
+ """Encrypts a refresh token before saving to DB."""
26
+ return fernet.encrypt(token.encode()).decode()
27
+
28
+
29
+ def decrypt_token(token: str) -> str:
30
+ """Decrypts a stored refresh token when needed."""
31
+ return fernet.decrypt(token.encode()).decode()
32
+
33
  bearer_scheme = HTTPBearer()
34
 
35
  SECRET_KEY = settings.SECRET_KEY
src/profile/utils.py CHANGED
@@ -12,17 +12,27 @@ from datetime import date
12
  from typing import Tuple, Optional, List
13
  from sqlmodel import select
14
  from sqlmodel.ext.asyncio.session import AsyncSession
15
- from src.core.models import UserTeamsRole, Roles, Users, Teams # adjust import path if differs
 
 
 
 
 
16
  from src.core.config import settings # for FCM key if needed
17
  import httpx
18
- import math
19
 
20
- def calculate_days(from_date: date, to_date: date, include_weekends: bool = True) -> int:
 
 
 
21
  """Calculate inclusive days. If you want to exclude weekends, add logic."""
22
  delta = (to_date - from_date).days + 1
23
  return max(0, delta)
24
 
25
- async def find_mentor_and_lead(session: AsyncSession, user_id) -> Tuple[Optional[dict], Optional[dict]]:
 
 
 
26
  """
27
  Return (mentor_user, lead_user) as dicts or None.
28
  Uses your existing UserTeamsRole and Roles tables to find role members in same team.
@@ -34,41 +44,51 @@ async def find_mentor_and_lead(session: AsyncSession, user_id) -> Tuple[Optional
34
  return None, None
35
 
36
  # 2) find Mentor role id
37
- mentor_role = (await session.exec(select(Roles).where(Roles.name == "Mentor"))).first()
38
- lead_role = (await session.exec(select(Roles).where(Roles.name == "Team Lead"))).first()
 
 
 
 
39
 
40
  mentor_user = None
41
  lead_user = None
42
 
43
  if mentor_role:
44
- mentor_user = (await session.exec(
45
- select(Users)
46
- .join(UserTeamsRole)
47
- .where(UserTeamsRole.team_id == user_team.team_id)
48
- .where(UserTeamsRole.role_id == mentor_role.id)
49
- )).first()
 
 
50
 
51
  if lead_role:
52
- lead_user = (await session.exec(
53
- select(Users)
54
- .join(UserTeamsRole)
55
- .where(UserTeamsRole.team_id == user_team.team_id)
56
- .where(UserTeamsRole.role_id == lead_role.id)
57
- )).first()
 
 
58
 
59
  return mentor_user, lead_user
60
 
61
 
62
-
63
  async def get_tokens_for_user(session: AsyncSession, user_id) -> list[str]:
64
  user = await session.get(Users, user_id)
65
  if not user:
66
  return []
67
  return user.device_tokens or []
68
 
 
69
  # Simple FCM send using legacy HTTP API (server key).
70
  # In production prefer FCM HTTP v1 (OAuth) or firebase-admin SDK.
71
- async def send_push_to_tokens(tokens: list[str], title: str, body: str, data: dict = None):
 
 
72
  if not tokens:
73
  return
74
 
@@ -97,8 +117,6 @@ async def send_push_to_tokens(tokens: list[str], title: str, body: str, data: di
97
  print("FCM send failed:", r.status_code, r.text)
98
 
99
 
100
-
101
-
102
  SMTP_HOST = settings.EMAIL_SERVER
103
  SMTP_PORT = settings.EMAIL_PORT
104
  SMTP_USER = settings.EMAIL_USERNAME
 
12
  from typing import Tuple, Optional, List
13
  from sqlmodel import select
14
  from sqlmodel.ext.asyncio.session import AsyncSession
15
+ from src.core.models import (
16
+ UserTeamsRole,
17
+ Roles,
18
+ Users,
19
+ Teams,
20
+ ) # adjust import path if differs
21
  from src.core.config import settings # for FCM key if needed
22
  import httpx
 
23
 
24
+
25
+ def calculate_days(
26
+ from_date: date, to_date: date, include_weekends: bool = True
27
+ ) -> int:
28
  """Calculate inclusive days. If you want to exclude weekends, add logic."""
29
  delta = (to_date - from_date).days + 1
30
  return max(0, delta)
31
 
32
+
33
+ async def find_mentor_and_lead(
34
+ session: AsyncSession, user_id
35
+ ) -> Tuple[Optional[dict], Optional[dict]]:
36
  """
37
  Return (mentor_user, lead_user) as dicts or None.
38
  Uses your existing UserTeamsRole and Roles tables to find role members in same team.
 
44
  return None, None
45
 
46
  # 2) find Mentor role id
47
+ mentor_role = (
48
+ await session.exec(select(Roles).where(Roles.name == "Mentor"))
49
+ ).first()
50
+ lead_role = (
51
+ await session.exec(select(Roles).where(Roles.name == "Team Lead"))
52
+ ).first()
53
 
54
  mentor_user = None
55
  lead_user = None
56
 
57
  if mentor_role:
58
+ mentor_user = (
59
+ await session.exec(
60
+ select(Users)
61
+ .join(UserTeamsRole)
62
+ .where(UserTeamsRole.team_id == user_team.team_id)
63
+ .where(UserTeamsRole.role_id == mentor_role.id)
64
+ )
65
+ ).first()
66
 
67
  if lead_role:
68
+ lead_user = (
69
+ await session.exec(
70
+ select(Users)
71
+ .join(UserTeamsRole)
72
+ .where(UserTeamsRole.team_id == user_team.team_id)
73
+ .where(UserTeamsRole.role_id == lead_role.id)
74
+ )
75
+ ).first()
76
 
77
  return mentor_user, lead_user
78
 
79
 
 
80
  async def get_tokens_for_user(session: AsyncSession, user_id) -> list[str]:
81
  user = await session.get(Users, user_id)
82
  if not user:
83
  return []
84
  return user.device_tokens or []
85
 
86
+
87
  # Simple FCM send using legacy HTTP API (server key).
88
  # In production prefer FCM HTTP v1 (OAuth) or firebase-admin SDK.
89
+ async def send_push_to_tokens(
90
+ tokens: list[str], title: str, body: str, data: dict = None
91
+ ):
92
  if not tokens:
93
  return
94
 
 
117
  print("FCM send failed:", r.status_code, r.text)
118
 
119
 
 
 
120
  SMTP_HOST = settings.EMAIL_SERVER
121
  SMTP_PORT = settings.EMAIL_PORT
122
  SMTP_USER = settings.EMAIL_USERNAME