Skip to content

Commit 003d21f

Browse files
committed
Refactor device endpoints and schemas for improved clarity and functionality
1 parent 7a53301 commit 003d21f

1 file changed

Lines changed: 102 additions & 18 deletions

File tree

app/api/v1/devices.py

Lines changed: 102 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
from fastapi import APIRouter, Depends, HTTPException
22
from sqlalchemy.orm import Session
33
from pydantic import BaseModel
4-
from typing import Optional
4+
from typing import Optional, List
55

6-
# --- IMPORT RATE LIMITER ---
6+
# --- IMPORT RATE LIMITER (Anti-Spam) ---
77
from fastapi_limiter.depends import RateLimiter
88

9-
# --- IMPORT AUTH (FIX PATH) ---
10-
from app.api.v1.auth import get_current_user # <-- Path harus 'app.auth'
9+
# --- IMPORT AUTH (Satpam Firebase) ---
10+
from app.api.v1.auth import get_current_user
1111

12-
# --- IMPORT DATABASE (FIX PATH) ---
13-
from app.core.database import get_db # <-- Path harus 'app.db.database'
12+
# --- IMPORT DATABASE ---
13+
from app.core.database import get_db
1414
from app.models.device import Device
1515

16-
# --- IMPORT MQTT ---
16+
# --- IMPORT MQTT (Buat Control) ---
1717
from app.mqtt.client import mqtt_client
1818

1919
router = APIRouter()
2020

21-
# --- SCHEMA ---
21+
# ==========================================
22+
# 1. SCHEMAS
23+
# ==========================================
2224
class UserClaimSchema(BaseModel):
2325
device_id: str
2426
pin_code: str
@@ -28,12 +30,42 @@ class AutoRegisterSchema(BaseModel):
2830
pin_code: str
2931
factory_secret: str
3032

31-
# --- 1. ENDPOINT CLAIM (User) ---
33+
class UpdateDeviceSchema(BaseModel):
34+
device_name: str
35+
36+
class DeviceResponse(BaseModel):
37+
device_id: str
38+
device_name: str
39+
owner_uid: Optional[str] = None
40+
41+
class Config:
42+
from_attributes = True
43+
44+
# ==========================================
45+
# 2. ENDPOINTS
46+
# ==========================================
47+
48+
# --- A. LIST MY DEVICES (Dashboard) ---
49+
# Limit: 20 request / menit (Karena sering direfresh user)
50+
@router.get("/my-devices", response_model=List[DeviceResponse], dependencies=[Depends(RateLimiter(times=20, seconds=60))])
51+
def get_my_devices(
52+
db: Session = Depends(get_db),
53+
user_uid: str = Depends(get_current_user) # 🔒 Wajib Login
54+
):
55+
"""
56+
Mengambil daftar alat milik user yang sedang login.
57+
"""
58+
devices = db.query(Device).filter(Device.owner_uid == user_uid).all()
59+
return devices
60+
61+
62+
# --- B. CLAIM DEVICE ---
63+
# Limit: 5 request / menit (Biar gak brute-force PIN)
3264
@router.post("/claim", dependencies=[Depends(RateLimiter(times=5, seconds=60))])
3365
def claim_device(
3466
claim_data: UserClaimSchema,
3567
db: Session = Depends(get_db),
36-
user_uid: str = Depends(get_current_user) # <-- Wajib Login
68+
user_uid: str = Depends(get_current_user) # 🔒 Wajib Login
3769
):
3870
clean_id = claim_data.device_id.strip().upper()
3971
clean_pin = claim_data.pin_code.strip()
@@ -43,7 +75,6 @@ def claim_device(
4375
if not device:
4476
raise HTTPException(status_code=404, detail=f"Alat {clean_id} tidak ditemukan.")
4577

46-
# Cek apakah sudah ada yang punya
4778
if device.owner_uid:
4879
if device.owner_uid == user_uid:
4980
return {"message": "Alat ini memang sudah punya kamu kok."}
@@ -60,13 +91,66 @@ def claim_device(
6091
return {"status": "success", "message": f"Alat {clean_id} berhasil diklaim!"}
6192

6293

63-
# --- 2. ENDPOINT CONTROL RELAY ---
64-
@router.post("/control-relay", dependencies=[Depends(RateLimiter(times=10, seconds=60))])
94+
# --- C. UPDATE DEVICE NAME ---
95+
# Limit: 10 request / menit
96+
@router.put("/{device_id}", response_model=DeviceResponse, dependencies=[Depends(RateLimiter(times=10, seconds=60))])
97+
def update_device_name(
98+
device_id: str,
99+
update_data: UpdateDeviceSchema,
100+
db: Session = Depends(get_db),
101+
user_uid: str = Depends(get_current_user) # 🔒 Wajib Login
102+
):
103+
clean_id = device_id.strip().upper()
104+
105+
device = db.query(Device).filter(Device.device_id == clean_id).first()
106+
107+
if not device:
108+
raise HTTPException(status_code=404, detail="Alat tidak ditemukan")
109+
110+
if device.owner_uid != user_uid:
111+
raise HTTPException(status_code=403, detail="Bukan alat kamu!")
112+
113+
device.device_name = update_data.device_name
114+
db.commit()
115+
db.refresh(device)
116+
117+
return device
118+
119+
120+
# --- D. UNCLAIM / DELETE DEVICE ---
121+
# Limit: 5 request / menit (Tindakan berbahaya/kritis)
122+
@router.delete("/{device_id}", dependencies=[Depends(RateLimiter(times=5, seconds=60))])
123+
def unclaim_device(
124+
device_id: str,
125+
db: Session = Depends(get_db),
126+
user_uid: str = Depends(get_current_user) # 🔒 Wajib Login
127+
):
128+
clean_id = device_id.strip().upper()
129+
130+
device = db.query(Device).filter(Device.device_id == clean_id).first()
131+
132+
if not device:
133+
raise HTTPException(status_code=404, detail="Alat tidak ditemukan")
134+
135+
if device.owner_uid != user_uid:
136+
raise HTTPException(status_code=403, detail="Bukan alat kamu!")
137+
138+
# Reset kepemilikan
139+
device.owner_uid = None
140+
device.device_name = "Unclaimed Device"
141+
db.commit()
142+
143+
return {"message": f"Alat {clean_id} berhasil dihapus dari akunmu."}
144+
145+
146+
# --- E. CONTROL RELAY ---
147+
# Limit: 20 request / menit (Biar bisa on/off agak cepat)
148+
@router.post("/control-relay", dependencies=[Depends(RateLimiter(times=20, seconds=60))])
65149
def control_relay(
66150
device_id: str,
67151
state: str,
68152
db: Session = Depends(get_db),
69-
user_uid: str = Depends(get_current_user) # <-- Wajib Login
153+
user_uid: str = Depends(get_current_user) # 🔒 Wajib Login
70154
):
71155
clean_id = device_id.strip().upper()
72156

@@ -78,10 +162,8 @@ def control_relay(
78162
if not device:
79163
raise HTTPException(status_code=404, detail="Alat tidak ditemukan.")
80164

81-
# --- SECURITY CHECK (PENTING!) ---
82-
# Pastikan yang request adalah pemilik alat
83165
if device.owner_uid != user_uid:
84-
raise HTTPException(status_code=403, detail="Eits! Bukan alat kamu, jangan iseng ya!")
166+
raise HTTPException(status_code=403, detail="Eits! Bukan alat kamu.")
85167

86168
topic = f"alat/{clean_id}/command"
87169
payload = f'{{"relay": "{state}"}}'
@@ -91,7 +173,9 @@ def control_relay(
91173
return {"message": "Perintah dikirim", "topic": topic, "state": state}
92174

93175

94-
# --- 3. ENDPOINT AUTO REGISTER (ESP32 - Machine to Machine) ---
176+
# --- F. AUTO REGISTER (Mesin ke Mesin) ---
177+
# Limit: 5 request / menit
178+
# TIDAK PAKAI 'user_uid' KARENA YANG REQUEST ADALAH ESP32 (Bukan Manusia)
95179
@router.post("/auto-register", dependencies=[Depends(RateLimiter(times=5, seconds=60))])
96180
def auto_register_device(
97181
data: AutoRegisterSchema,

0 commit comments

Comments
 (0)