-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
409 lines (347 loc) · 13.2 KB
/
main.py
File metadata and controls
409 lines (347 loc) · 13.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
import uvicorn
from fastapi import (
FastAPI,
HTTPException,
Response,
Cookie,
Security,
status,
Request
)
from fastapi.middleware.cors import CORSMiddleware
from utils.entities import UserDataM, TenantM, OwnerM
from utils.controller import UserDataC, TenantC, PropertyC, OwnerC
from utils.controller import migrationC
from typing import Annotated
import logging
from opencensus.ext.azure.log_exporter import AzureLogHandler
from opencensus.ext.azure.trace_exporter import AzureExporter
from opencensus.trace.samplers import ProbabilitySampler
from opencensus.ext.fastapi.fastapi_middleware import FastAPIMiddleware
# Replace this with your Application Insights Instrumentation Key
INSTRUMENTATION_KEY = ""
# Set up logging to Azure Application Insights
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.addHandler(AzureLogHandler(connection_string=f'InstrumentationKey={INSTRUMENTATION_KEY}'))
app = FastAPI(
title="NightON-API",
description="RestAPI qui fait le pont entre les applications clientes et la bdd.",
version="1.0.0",
)
# Configuration CORS pour gérer les accès au web service
# middleware : fonction qui s'exécute à chaque appel d'un endpoint
origins = ["*"] # Ajoutez ici vos origines autorisées
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Set up tracing with Azure Application Insights
app.add_middleware(
FastAPIMiddleware,
exporter=AzureExporter(connection_string=f'InstrumentationKey={INSTRUMENTATION_KEY}'),
sampler=ProbabilitySampler(1.0), # 1.0 = 100% sampling rate, you can reduce for lower load.
)
@app.get("/", response_model=dict, tags=['Entrypoint'])
async def start():
"""
Point de départ de nightON API.
"""
logger.info("Received request to /")
try:
migrationC.run_migration(dbname="nighton_db")
state = "ready"
logger.info("Database ready !")
except Exception as e:
state = "K.O."
logger.info(f"Database K.O : an error occured : {e}")
return {
"Message pour vous": "Bonjour cher développeur, bienvenue dans la politique de confidentialité de nightON",
"Politique de confidentialité": "A venir",
"Dernière màj": "06/03/2024",
#'header': req.headers.get('authorization'), # enlever
#"API Key": generateJWT(role_id="nightOnWebSiteApp"),
"API Key": 'Not available.',
"DB STATE": state
}
@app.post("/register", tags=["Creation de compte"])
async def registerUser(user_data: UserDataM.ClassUserDataM):
"""
Créer nouvel utilisateur.
--------------------------------------
{"id_user": "",
"firstname_user": "Joel",
"lastname_user": "Duval",
"birthdate_user": "",
"email_user": "titouchkpoubelle@gmail.com",
"telephone_user": "",
"pays": "",
"code_postal": "",
"ville": "",
"numero_rue": "",
"nom_rue": "",
"complement_adresse_1": "",
"complement_adresse_2": "",
"url1_piece": "",
"url2_piece": ""}
"""
res = UserDataC.ClassUserDataC.addOneUser(obj_user=user_data)
if res=="USER ALREADY EXISTS":
raise HTTPException(status_code=500, detail="UTILISATEUR EXISTE DEJA.")
return {"API rep": res}
@app.get("/login/request/{email_user}", tags=["Connexion"])
async def loginRequest(email_user: str):
"""
Demande de login utilisateur.
Envoie un code dans l'email.
"""
res = UserDataC.ClassUserDataC.loginRequest(email_user)
if res:
# si res est null -> tt s'est bien passé.
return {"API rep": res}
return {"API rep": "Vérifiez le mail envoyé."}
@app.get("/login/auth/{email_user}/{code}", tags=["Connexion"])
async def loginAuthentification(email_user: str, code: str, response: Response):
"""
Authentification utilisateur.
----------------------------
@param email : email utilisateur.
@param code : code 5 chiffres reçu par mail.
@return : session id.
"""
res = UserDataC.ClassUserDataC.loginAuth(email_user, code)
if not res:
raise HTTPException(
status_code=403, detail="Please enter correct email or code"
)
# !! a modif apres pour sécu :: hash reversible, jwt token
response.set_cookie(
key="connected_cookie", value=f"yes_{email_user}", expires=60 * 60
)
return {"API rep": f"Bienvenue {email_user} !"}
@app.get("/auth/from_firebase/{email_user}", tags=["Connexion"])
async def firebaseLoginAuthentification(email_user: str, response: Response):
"""
Route pour les cas firebase.
Pas de distinction entre register et login car email déjà certifié.
Pas de code.
Authentification utilisateur en passant par firebase.
----------------------------
:param email : email utilisateur.
:return : session id.
----------------------------
{"id_user": "",
"firstname_user": "",
"lastname_user": "",
"birthdate_user": "",
"email_user": "titouan.scht@gmail.com",
"telephone_user": "",
"pays": "",
"code_postal": "",
"ville": "",
"numero_rue": "",
"nom_rue": "",
"complement_adresse_1": "",
"complement_adresse_2": "",
"url1_piece": "",
"url2_piece": ""}
"""
# verifier existance.
res = UserDataC.ClassUserDataC.loginAuth(email_user, from_firebase=True)
if not res:
# si non creer compte juste à partir de l'adresse mail + noms fictifs (modif après)
user_data = UserDataM.ClassUserDataM(
firstname_user="FUser0000", lastname_user="LUser0000", email_user=email_user
)
UserDataC.ClassUserDataC.addOneUser(obj_user=user_data)
# !! a modif apres pour sécu :: hash reversible, jwt token
response.set_cookie(
key="connected_cookie", value=f"yes_{email_user}", expires=60 * 60
)
return {"API rep": f"Bienvenue {email_user} !"}
### PROFIL
@app.get("/auth_users/display_me", tags=["Profil Utilisateur"])
def funcGetMe(connected_cookie: Annotated[str, Cookie()] = None):
"""
Se base sur le cookie connecté pour afficher les userData de l'utilisateur.
"""
if connected_cookie is None:
raise HTTPException(status_code=403, detail="Please connect before.")
# email stocké dans le cookie
email_user = connected_cookie.split("_")[-1]
me = UserDataC.ClassUserDataC.findOneByEmail(email_user)
return me
@app.get("/auth_users/welcome", tags=["Connexion"])
# mettre une dépendance a loginAuth
async def funcWelcomeUser(
current_user: Annotated[UserDataC.ClassUserDataM, Security(funcGetMe)]
):
"""
Affiche un message de bienvenue à l'utilisateur.
Dépend de l'authentification de l'utilisateur.
"""
return {"API rep": f"Bienvenue à toi {current_user.firstname_user} !"}
@app.post("/auth_users/update_me", tags=["Profil Utilisateur"])
async def updateUserProfil(
new_user: UserDataM.ClassUserDataM,
current_user: Annotated[UserDataC.ClassUserDataM, Security(funcGetMe)],
):
"""
Mettre à jour un profil utilisateur.
-------------------------------------------
{"id_user": "",
"firstname_user": "Youssef",
"lastname_user": "LUser0000",
"birthdate_user": "",
"email_user": "titouan.scht@gmail.com",
"telephone_user": "",
"pays": "",
"code_postal": "",
"ville": "",
"numero_rue": "",
"nom_rue": "",
"complement_adresse_1": "",
"complement_adresse_2": "",
"url1_piece": "",
"url2_piece": ""}
"""
res = UserDataC.ClassUserDataC.updateUserData(obj_user=new_user)
return {"API rep": res}
@app.delete("/auth_users/delete/{email_user}", tags=["Profil Utilisateur"])
async def deleteUser(email_user: str):
"""
Supprimer utilisateur.
TODO
"""
return "Not ready"
######### DEMANDES CLIENT ############
@app.post("/users/demande_reservation", tags=["Reservation logement"])
# on peut récupérer son email de son cookie de connexion
# il faudrait pouvoir récupérer l'id_property
async def registerTenant(
new_tenant: TenantM.ClassTenantRegisteringM,
connected_cookie: Annotated[str, Cookie()] = None,
current_property_cookie: Annotated[str, Cookie()] = None,
):
"""
Créer une nouvelle demande pour être locataire.
On a un statut 'waiting' au départ
"""
if current_property_cookie is None:
raise HTTPException(
status_code=500, detail="Internal Error cannot identify property."
)
if connected_cookie is None:
raise HTTPException(status_code=403, detail="Please connect before.")
# email stocké dans le cookie
email_user = connected_cookie.split("_")[-1]
id_property = current_property_cookie.split("@")[-1]
# me = UserDataC.ClassUserDataC.findOneByEmail(email_user)
# identifie le user dans la bdd, retourne UTILISATEUR NON ENREGISTRE si mauvais cookie
new_tenant.email_user = email_user
new_tenant.id_logement = id_property
res = TenantC.ClassTenantC.addOneTenant(objIns=new_tenant)
return {"API rep": res}
@app.post("/users/demande_publication", tags=["Publication logement"])
# on devrait pouvoir récupérer son email de son cookie de connexion
async def registerOwner(
new_owner: OwnerM.ClassOwnerRegisteringM,
connected_cookie: Annotated[str, Cookie()] = None,
):
"""
Créer une nouvelle demande pour publier son logement.
On a un statut 'waiting' au départ.
"""
if connected_cookie is None:
raise HTTPException(status_code=403, detail="Please connect before.")
email_user = connected_cookie.split("_")[-1]
# identifie le user dans la bdd, retourne UTILISATEUR NON ENREGISTRE si mauvais cookie
new_owner.email_user = email_user
res = OwnerC.ClassOwnerC.addOneOnwer(objIns=new_owner)
return {"API rep": res}
####### BIENS A LOUER ##############
@app.get("/accueil", tags=["Page accueil"])
async def displayAll():
"""Affichage par defaut, overview des logements."""
res = PropertyC.ClassPropertyC.displayAll()
return {"API rep": res}
@app.get("/accueil/{id_property}", tags=["Page accueil"])
async def displayPropertyDetails_2(id_property: str, response: Response):
"""Afficher les details d'un logement par son id.
Btw placer un cookie qui correspond au logement current.
"""
res = PropertyC.ClassPropertyC.displayPropertyById(
id=id_property
)
response.set_cookie(
key="current_property_cookie", value=f"prop@{id_property}", expires=60* 60
)
return {"API rep": res}
######## ADMIN: VALIDATION DES DEMANDES ##########
from enum import Enum
class status(Enum):
c = "cancel"
w = "waiting"
a = "approve"
@classmethod
def all(cls):
return [status.a.value, status.w.value, status.c.value]#, status.d.value]
# send mail to proprio
# validate owner
# validate tenant -> create contrat
@app.get("/approvals/reservation/{property_id}/{tenant_id}/{new_status}", tags=["SysAdmin-Validation/Refus demande"])
def changeStatusTenantDemand(
tenant_id: str = None, property_id: str = None, new_status: str = None
):
if tenant_id and new_status:
new_status = new_status.lower()
if new_status not in status.all():
raise HTTPException(
status_code=401, detail=f"Wrong status. Choose between {status.all()}"
)
if new_status == status.c:
res = TenantC.ClassTenantC.deleteTenant(tenant_id)
if new_status == status.a:
res = TenantC.ClassTenantC.validateTenant(tenant_id, property_id)
else:
res = "Please provide tenant_id and/or new_status."
return {"API rep": res}
@app.get("/approvals/publication/{owner_id}/{property_id}/{new_status}", tags=["SysAdmin-Validation/Refus demande"])
def changeStatusOwnerDemand(owner_id, property_id, new_status):
if owner_id and new_status:
new_status = new_status.lower()
if new_status not in status.all():
raise HTTPException(
status_code=401, detail=f"Wrong status. Choose between {status.all()}"
)
if new_status == 'canceled':
res = OwnerC.ClassOwnerC.deleteOwner(owner_id, property_id)
if new_status == 'approve':
res = OwnerC.ClassOwnerC.validateOwner(owner_id, property_id)
else:
res = "Please provide owner_id and/or new_status."
return {"API rep": res}
@app.get("/approvals/publication/show_all", tags=["SysAdmin-Validation/Refus demande"])
def showAllDmdPubli():
res = OwnerC.ClassOwnerC.findAllWaitingOwners()
return {"API rep": res}
@app.get("/approvals/reservation/show_all", tags=["SysAdmin-Validation/Refus demande"])
def showAllDmdResa():
res = TenantC.ClassTenantC.findAllWaitingTenants()
return {"API rep": res}
@app.post("/approvals/reservation/send_notification", tags=["SysAdmin-Validation/Refus demande"])
def sendNotifToOwner():
"""Envoyer un mail de notification aux proprio qd il ya une demande de resa."""
content = ""
pass
@app.post("/approvals/reservation/send_notification", tags=["SysAdmin-Validation/Refus demande"])
def sendNotifToTenant():
"""Envoyer un mail de confirmation avec un certain contenu."""
content = ""
pass
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000) # , reload=True)
#uvicorn.run(app, host="0.0.0.0", port=8000) # , reload=True)