AlejandroCalizaya commited on
Commit
9c57563
·
1 Parent(s): 0ebd2ee

chore: initial config

Browse files
Files changed (5) hide show
  1. app/__init__.py +0 -0
  2. app/main.py +170 -0
  3. app/models.py +8 -0
  4. app/utils.py +8 -0
  5. requirements.txt +37 -0
app/__init__.py ADDED
File without changes
app/main.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sendgrid
3
+ from sendgrid.helpers.mail import Mail
4
+ from fastapi import FastAPI, HTTPException
5
+ from supabase import create_client
6
+ from dotenv import load_dotenv
7
+ from datetime import datetime, timedelta
8
+
9
+ from app.utils import *
10
+ from app.models import *
11
+
12
+
13
+ load_dotenv()
14
+
15
+ # Supabase configuration
16
+ SUPABASE_URL = os.getenv("SUPABASE_URL")
17
+ SUPABASE_KEY = os.getenv("SUPABASE_KEY")
18
+
19
+ supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
20
+
21
+
22
+ # SendGrid configuration
23
+ SENDGRID_API_KEY = os.getenv("SENDGRID_API_KEY")
24
+ SENDGRID_SENDER = os.getenv("SENDGRID_SENDER")
25
+
26
+ sg = sendgrid.SendGridAPIClient(api_key=SENDGRID_API_KEY)
27
+
28
+
29
+ # JWT Configuration
30
+ JWT_EXPIRES_IN = os.getenv("JWT_EXPIRES_IN")
31
+
32
+
33
+ app = FastAPI()
34
+
35
+
36
+ @app.get("/users")
37
+ def get_users():
38
+ response = supabase.table("users").select("*").execute()
39
+ return response.data
40
+
41
+
42
+ @app.post("/add_user")
43
+ def add_user(email: str):
44
+ response = supabase.table("users").insert({
45
+ "email": email
46
+ }).execute()
47
+ return {"status": "ok", "inserted": response.data}
48
+
49
+ @app.post("/auth/send-verification")
50
+ def send_verification(data: EmailRequest):
51
+ email = data.email
52
+
53
+ # Validate email domain
54
+ if not is_valid_email(email):
55
+ raise HTTPException(status_code=400, detail="Email no válido o dominio no permitido")
56
+
57
+ # Rate limiting: check last code sent time
58
+ last_code = (
59
+ supabase.table("auth_code")
60
+ .select("*")
61
+ .eq("email", email)
62
+ .order("created_at", desc=True)
63
+ .limit(1)
64
+ .execute()
65
+ )
66
+
67
+ if last_code.data:
68
+ created_at = datetime.fromisoformat(last_code.data[0]["created_at"].replace("Z", ""))
69
+ diff = datetime.utcnow() - created_at
70
+
71
+ if diff.total_seconds() < 60:
72
+ raise HTTPException(status_code=429, detail="Demasiados intentos, espera 60 segundos")
73
+
74
+ # Generate verification code
75
+ code = generate_code()
76
+ expires_at = datetime.utcnow() + timedelta(seconds=int(JWT_EXPIRES_IN))
77
+
78
+ # Store code in the database
79
+ supabase.table("auth_code").delete().eq("email", email).execute()
80
+
81
+ supabase.table("auth_code").insert({
82
+ "email": email,
83
+ "code": code,
84
+ "expires_at": expires_at.isoformat()
85
+ }).execute()
86
+
87
+ # Send email with SendGrid
88
+ message = Mail(
89
+ from_email=SENDGRID_SENDER,
90
+ to_emails=email,
91
+ subject="Your Verification Code",
92
+ html_content=f"<h1>{code}</h1><p>Your verification code expires in {JWT_EXPIRES_IN} seconds.</p>"
93
+ )
94
+
95
+ try:
96
+ sg.send(message)
97
+ except Exception as e:
98
+ raise HTTPException(status_code=500, detail="Error sending email")
99
+
100
+ return {
101
+ "success": True,
102
+ "message": f"Código enviado a {email}",
103
+ "expires_in": JWT_EXPIRES_IN
104
+ }
105
+
106
+
107
+ @app.post("/auth/verify")
108
+ def verify(data: VerifyRequest):
109
+ email = data.email
110
+ code = data.code
111
+
112
+ # Verify if the email exists
113
+ record = (
114
+ supabase.table("auth_code")
115
+ .select("*")
116
+ .eq("email", email)
117
+ .execute()
118
+ )
119
+
120
+ if not record.data:
121
+ raise HTTPException(status_code=404, detail="Email no encontrado")
122
+
123
+ # Verify if the code matches
124
+ if record.data[0]["code"] != code:
125
+ raise HTTPException(status_code=400, detail="Código inválido")
126
+
127
+ # Verify if the code has expired
128
+ expires_at = datetime.fromisoformat(record.data[0]["expires_at"].replace("Z", ""))
129
+ if datetime.utcnow() > expires_at:
130
+ raise HTTPException(status_code=400, detail="Código expirado")
131
+
132
+ # Get or create user
133
+ user_record = (
134
+ supabase.table("users")
135
+ .select("*")
136
+ .eq("email", email)
137
+ .execute()
138
+ )
139
+
140
+ supabase.table("auth_code").delete().eq("email", email).execute()
141
+
142
+ if not user_record.data:
143
+ user = supabase.table("users").insert({
144
+ "email": email
145
+ }).execute().data[0]
146
+
147
+ return {
148
+ "success": True,
149
+ "user": {
150
+ "id": f"uuid-{user['id']}",
151
+ "email": user["email"],
152
+ "firstName": None,
153
+ "lastName": None,
154
+ "onboardingCompleted": False
155
+ }
156
+ }
157
+
158
+
159
+ user = user_record.data[0]
160
+
161
+ return {
162
+ "success": True,
163
+ "user": {
164
+ "id": f"uuid-{user['id']}",
165
+ "email": user['email'],
166
+ "firstName": user['firstName'],
167
+ "lastName": user['lastName'],
168
+ "onboardingCompleted": user['onboardingCompleted']
169
+ }
170
+ }
app/models.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, EmailStr
2
+
3
+ class EmailRequest(BaseModel):
4
+ email: EmailStr
5
+
6
+ class VerifyRequest(BaseModel):
7
+ email: EmailStr
8
+ code: str
app/utils.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ import random
2
+
3
+ def generate_code() -> str:
4
+ # Create a random 6-digit verification code: 000000 to 999999
5
+ return f"{random.randint(0, 999999):06}"
6
+
7
+ def is_valid_email(email: str) -> bool:
8
+ return email.endswith("@utec.edu.pe")
requirements.txt ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -----------------------------
2
+ # 🚀 Server and API
3
+ # -----------------------------
4
+ fastapi
5
+ uvicorn
6
+
7
+ # -----------------------------
8
+ # ⚙️ Configuration and Environment Variables
9
+ # -----------------------------
10
+ python-dotenv
11
+
12
+ # -----------------------------
13
+ # 🗄️ Database (Supabase)
14
+ # -----------------------------
15
+ supabase
16
+
17
+ # -----------------------------
18
+ # 🔐 JWT Tokens and Security
19
+ # -----------------------------
20
+ python-jose
21
+ passlib[bcrypt]
22
+
23
+ # -----------------------------
24
+ # 📤 Email Sending (SendGrid)
25
+ # -----------------------------
26
+ sendgrid
27
+
28
+ # -----------------------------
29
+ # 🧩 Validations and Models
30
+ # -----------------------------
31
+ pydantic
32
+ email-validator
33
+
34
+ # -----------------------------
35
+ # 📨 Optional: Email Templates (HTML)
36
+ # -----------------------------
37
+ jinja2