Spaces:
Runtime error
Runtime error
Commit
·
5cd9bab
1
Parent(s):
9d46fc2
Add email verification on singup
Browse files- TechdocsAPI/backend/__init__.py +24 -0
- TechdocsAPI/backend/core/ConfigEnv.py +5 -0
- TechdocsAPI/backend/core/ExceptionHandlers.py +5 -0
- TechdocsAPI/backend/core/Exceptions.py +12 -0
- TechdocsAPI/backend/router.py +12 -3
- TechdocsAPI/backend/services/auth/ops.py +65 -6
- TechdocsAPI/backend/services/auth/utils/auth_funcs.py +16 -29
- TechdocsAPI/backend/templates/email_verification.html +68 -0
- TechdocsAPI/backend/templates/verification_failure.html +33 -0
- TechdocsAPI/backend/templates/verification_success.html +33 -0
- TechdocsAPI/requirements.txt +1 -0
- scripts/test.py +11 -10
TechdocsAPI/backend/__init__.py
CHANGED
@@ -3,6 +3,7 @@ from mysql.connector import errorcode
|
|
3 |
|
4 |
from fastapi import FastAPI, status
|
5 |
from fastapi.exceptions import HTTPException
|
|
|
6 |
|
7 |
from backend.utils import DBConnection
|
8 |
from backend.core.ConfigEnv import config
|
@@ -11,6 +12,8 @@ from langchain.llms import Clarifai
|
|
11 |
from langchain.chains import LLMChain
|
12 |
from langchain.prompts import PromptTemplate
|
13 |
|
|
|
|
|
14 |
app = FastAPI(title="Techdocs",
|
15 |
version="V0.0.1",
|
16 |
description="API for automatic code documentation generation!"
|
@@ -43,6 +46,27 @@ try:
|
|
43 |
app.state.llmchain = llmchain
|
44 |
|
45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
except mysql.connector.Error as err:
|
47 |
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(err))
|
48 |
|
|
|
3 |
|
4 |
from fastapi import FastAPI, status
|
5 |
from fastapi.exceptions import HTTPException
|
6 |
+
from fastapi.templating import Jinja2Templates
|
7 |
|
8 |
from backend.utils import DBConnection
|
9 |
from backend.core.ConfigEnv import config
|
|
|
12 |
from langchain.chains import LLMChain
|
13 |
from langchain.prompts import PromptTemplate
|
14 |
|
15 |
+
from fastapi_mail import ConnectionConfig, FastMail
|
16 |
+
|
17 |
app = FastAPI(title="Techdocs",
|
18 |
version="V0.0.1",
|
19 |
description="API for automatic code documentation generation!"
|
|
|
46 |
app.state.llmchain = llmchain
|
47 |
|
48 |
|
49 |
+
conf = ConnectionConfig(
|
50 |
+
MAIL_USERNAME=config.MAIL_USERNAME,
|
51 |
+
MAIL_PASSWORD=config.MAIL_PASSWORD,
|
52 |
+
MAIL_FROM=config.MAIL_FROM,
|
53 |
+
MAIL_PORT=587,
|
54 |
+
MAIL_SERVER="smtp.gmail.com",
|
55 |
+
MAIL_STARTTLS=True,
|
56 |
+
MAIL_SSL_TLS=False,
|
57 |
+
TEMPLATE_FOLDER="backend/templates",
|
58 |
+
USE_CREDENTIALS = True,
|
59 |
+
VALIDATE_CERTS = True
|
60 |
+
|
61 |
+
# MAIL_TLS=True,
|
62 |
+
# MAIL_SSL=False
|
63 |
+
)
|
64 |
+
|
65 |
+
app.state.mail_client = FastMail(conf)
|
66 |
+
app.state.templates = Jinja2Templates(directory="./backend/templates")
|
67 |
+
|
68 |
+
|
69 |
+
|
70 |
except mysql.connector.Error as err:
|
71 |
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(err))
|
72 |
|
TechdocsAPI/backend/core/ConfigEnv.py
CHANGED
@@ -13,12 +13,17 @@ class Settings(BaseSettings):
|
|
13 |
ALGORITHM:str
|
14 |
JWT_SECRET_KEY:str
|
15 |
JWT_REFRESH_SECRET_KEY:str
|
|
|
16 |
# OPENAI_KEY:str
|
17 |
APP_ID:str
|
18 |
USER_ID:str
|
19 |
MODEL_ID:str
|
20 |
CLARIFAI_PAT:str
|
21 |
MODEL_VERSION_ID:str
|
|
|
|
|
|
|
|
|
22 |
|
23 |
class Config:
|
24 |
env_file = ".env"
|
|
|
13 |
ALGORITHM:str
|
14 |
JWT_SECRET_KEY:str
|
15 |
JWT_REFRESH_SECRET_KEY:str
|
16 |
+
JWT_VERIFICATION_SECRET_KEY:str
|
17 |
# OPENAI_KEY:str
|
18 |
APP_ID:str
|
19 |
USER_ID:str
|
20 |
MODEL_ID:str
|
21 |
CLARIFAI_PAT:str
|
22 |
MODEL_VERSION_ID:str
|
23 |
+
|
24 |
+
MAIL_USERNAME:str
|
25 |
+
MAIL_PASSWORD:str
|
26 |
+
MAIL_FROM:str
|
27 |
|
28 |
class Config:
|
29 |
env_file = ".env"
|
TechdocsAPI/backend/core/ExceptionHandlers.py
CHANGED
@@ -13,6 +13,11 @@ async def handle_existing_user_found(request: Request, exec: ExistingUserExcepti
|
|
13 |
content=repr(exec)
|
14 |
)
|
15 |
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
@app.exception_handler(InvalidCredentialsException)
|
18 |
async def handle_login_failed(request: Request, exec: InvalidCredentialsException):
|
|
|
13 |
content=repr(exec)
|
14 |
)
|
15 |
|
16 |
+
@app.exception_handler(EmailNotVerifiedException)
|
17 |
+
async def email_not_verified(request: Request, exec: EmailNotVerifiedException):
|
18 |
+
return JSONResponse(status_code=status.HTTP_403_FORBIDDEN,
|
19 |
+
content=repr(exec)
|
20 |
+
)
|
21 |
|
22 |
@app.exception_handler(InvalidCredentialsException)
|
23 |
async def handle_login_failed(request: Request, exec: InvalidCredentialsException):
|
TechdocsAPI/backend/core/Exceptions.py
CHANGED
@@ -43,3 +43,15 @@ class InfoNotFoundException(Exception):
|
|
43 |
|
44 |
def __repr__(self):
|
45 |
return "exception.InfoNotFoundException()"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
|
44 |
def __repr__(self):
|
45 |
return "exception.InfoNotFoundException()"
|
46 |
+
|
47 |
+
|
48 |
+
class EmailNotVerifiedException(Exception):
|
49 |
+
def __init__(self):
|
50 |
+
self.set_statuses()
|
51 |
+
super(EmailNotVerifiedException, self).__init__()
|
52 |
+
|
53 |
+
def set_statuses(self):
|
54 |
+
self.status = 'EmailNotVerifiedException'
|
55 |
+
|
56 |
+
def __repr__(self):
|
57 |
+
return "exception.EmailNotVerifiedException()"
|
TechdocsAPI/backend/router.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
from fastapi import Request, Depends, UploadFile
|
2 |
from fastapi.middleware.cors import CORSMiddleware
|
3 |
|
4 |
from backend import app
|
@@ -42,12 +42,12 @@ def api_response_check():
|
|
42 |
return response_result
|
43 |
|
44 |
@app.post("/auth/signup", summary="Creates new user account", response_model=GeneralResponse, tags=["Auth Server"])
|
45 |
-
async def signup(response: UserAuth):
|
46 |
response_result = GeneralResponse.get_instance(data={},
|
47 |
status="not_allowed",
|
48 |
message=["Not authenticated"]
|
49 |
)
|
50 |
-
ops_signup(response_result, response)
|
51 |
|
52 |
return response_result
|
53 |
|
@@ -66,3 +66,12 @@ async def inference(generate: Generate, access_token:str=Depends(JWTBearer())):
|
|
66 |
user_sub=Auth.get_user_credentials(access_token)
|
67 |
|
68 |
return ops_inference(generate.code_block,generate.api_key,user_sub)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import Request, Depends, UploadFile, BackgroundTasks
|
2 |
from fastapi.middleware.cors import CORSMiddleware
|
3 |
|
4 |
from backend import app
|
|
|
42 |
return response_result
|
43 |
|
44 |
@app.post("/auth/signup", summary="Creates new user account", response_model=GeneralResponse, tags=["Auth Server"])
|
45 |
+
async def signup(bgtasks : BackgroundTasks, response: UserAuth):
|
46 |
response_result = GeneralResponse.get_instance(data={},
|
47 |
status="not_allowed",
|
48 |
message=["Not authenticated"]
|
49 |
)
|
50 |
+
await ops_signup(bgtasks, response_result, response)
|
51 |
|
52 |
return response_result
|
53 |
|
|
|
66 |
user_sub=Auth.get_user_credentials(access_token)
|
67 |
|
68 |
return ops_inference(generate.code_block,generate.api_key,user_sub)
|
69 |
+
|
70 |
+
@app.get("/auth/verify/{token}", summary="Verify Email", response_model=GeneralResponse, tags=["Auth Server"])
|
71 |
+
async def verify_email(request: Request, token:str):
|
72 |
+
response_result = GeneralResponse.get_instance(data={},
|
73 |
+
status="not_allowed",
|
74 |
+
message=["Not authenticated"]
|
75 |
+
)
|
76 |
+
|
77 |
+
return ops_verify_email(request, response_result,token)
|
TechdocsAPI/backend/services/auth/ops.py
CHANGED
@@ -4,12 +4,19 @@ from backend.models import *
|
|
4 |
from backend.services.db.utils.DBQueries import DBQueries
|
5 |
from backend.core.Exceptions import *
|
6 |
from backend.core.ExceptionHandlers import *
|
|
|
7 |
from backend import app
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
# import openai
|
10 |
# from transformers import RobertaTokenizer, T5ForConditionalGeneration
|
11 |
|
12 |
-
def ops_signup(response_result: GeneralResponse, data: UserAuth):
|
13 |
"""Wrapper method to handle signup process.
|
14 |
|
15 |
Args:
|
@@ -26,12 +33,31 @@ def ops_signup(response_result: GeneralResponse, data: UserAuth):
|
|
26 |
if len(list(user)) != 0:
|
27 |
# user with the entered credentials already exists
|
28 |
raise ExistingUserException(response_result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
-
DBQueries.insert_to_database('auth', (data.username, Auth.get_password_hash(data.password), data.email),
|
31 |
-
['username', 'password', 'email'])
|
32 |
|
33 |
response_result.status = 'success'
|
34 |
-
response_result.message = [f'
|
35 |
|
36 |
def ops_login(data:LoginCreds):
|
37 |
"""Wrapper method to handle login process.
|
@@ -50,7 +76,7 @@ def ops_login(data:LoginCreds):
|
|
50 |
status="not_allowed",
|
51 |
message=["Not authenticated"]
|
52 |
)
|
53 |
-
user = DBQueries.fetch_data_from_database('auth', ['username', 'password'], f"username='{data.username}'")
|
54 |
user = list(user)
|
55 |
if len(user) == 0:
|
56 |
# user with the entered credentials does not exist
|
@@ -60,9 +86,12 @@ def ops_login(data:LoginCreds):
|
|
60 |
# password is incorrect
|
61 |
raise InvalidCredentialsException(response_result)
|
62 |
|
|
|
|
|
|
|
63 |
# password is correct
|
64 |
return TokenSchema(access_token=Auth.create_access_token(data.username),
|
65 |
-
refresh_token=Auth.
|
66 |
)
|
67 |
|
68 |
def ops_regenerate_api_key(username:str) -> APIKey:
|
@@ -107,3 +136,33 @@ def ops_inference(source_code:str,api_key:str,username:str):
|
|
107 |
return docstring
|
108 |
|
109 |
return generate_docstring(source_code)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
from backend.services.db.utils.DBQueries import DBQueries
|
5 |
from backend.core.Exceptions import *
|
6 |
from backend.core.ExceptionHandlers import *
|
7 |
+
from backend.core.ConfigEnv import config
|
8 |
from backend import app
|
9 |
|
10 |
+
from fastapi import HTTPException, BackgroundTasks
|
11 |
+
from pydantic import ValidationError
|
12 |
+
from jose import jwt
|
13 |
+
|
14 |
+
from fastapi_mail import MessageSchema, MessageType
|
15 |
+
|
16 |
# import openai
|
17 |
# from transformers import RobertaTokenizer, T5ForConditionalGeneration
|
18 |
|
19 |
+
async def ops_signup(bgtasks: BackgroundTasks, response_result: GeneralResponse, data: UserAuth):
|
20 |
"""Wrapper method to handle signup process.
|
21 |
|
22 |
Args:
|
|
|
33 |
if len(list(user)) != 0:
|
34 |
# user with the entered credentials already exists
|
35 |
raise ExistingUserException(response_result)
|
36 |
+
verifiction_token = Auth.create_access_token(f"{data.username} {data.email}", secret_name='VERIFICATION')
|
37 |
+
verification_link = f"http://localhost:8000/auth/verify/{verifiction_token}"
|
38 |
+
|
39 |
+
email_body_params = {
|
40 |
+
"username": data.username,
|
41 |
+
"verify_link": verification_link
|
42 |
+
}
|
43 |
+
|
44 |
+
message = MessageSchema(
|
45 |
+
subject="Welcome to Techdocs:[Account Verification]",
|
46 |
+
recipients=[data.email], # List of recipients, as many as you can pass
|
47 |
+
template_body=email_body_params,
|
48 |
+
subtype=MessageType.html
|
49 |
+
)
|
50 |
+
|
51 |
+
bgtasks.add_task(app.state.mail_client.send_message, message=message, template_name="email_verification.html")
|
52 |
+
# await app.state.mail_client.send_message(message=message, template_name="email_verification.html")
|
53 |
+
|
54 |
+
DBQueries.insert_to_database('auth', (data.username, Auth.get_password_hash(data.password), "", 0),
|
55 |
+
['username', 'password', 'email', 'is_verified'])
|
56 |
+
|
57 |
|
|
|
|
|
58 |
|
59 |
response_result.status = 'success'
|
60 |
+
response_result.message = [f'Activate your account by clicking on the link sent to {data.email}.\nMake sure to check your spam folder.']
|
61 |
|
62 |
def ops_login(data:LoginCreds):
|
63 |
"""Wrapper method to handle login process.
|
|
|
76 |
status="not_allowed",
|
77 |
message=["Not authenticated"]
|
78 |
)
|
79 |
+
user = DBQueries.fetch_data_from_database('auth', ['username', 'password', 'is_verified'], f"username='{data.username}'")
|
80 |
user = list(user)
|
81 |
if len(user) == 0:
|
82 |
# user with the entered credentials does not exist
|
|
|
86 |
# password is incorrect
|
87 |
raise InvalidCredentialsException(response_result)
|
88 |
|
89 |
+
if not user[2]:
|
90 |
+
raise EmailNotVerifiedException()
|
91 |
+
|
92 |
# password is correct
|
93 |
return TokenSchema(access_token=Auth.create_access_token(data.username),
|
94 |
+
refresh_token=Auth.create_access_token(data.username, secret_name='REFRESH'),
|
95 |
)
|
96 |
|
97 |
def ops_regenerate_api_key(username:str) -> APIKey:
|
|
|
136 |
return docstring
|
137 |
|
138 |
return generate_docstring(source_code)
|
139 |
+
|
140 |
+
|
141 |
+
def ops_verify_email(request: Request, response_result: GeneralResponse, token:str):
|
142 |
+
try:
|
143 |
+
payload = jwt.decode(
|
144 |
+
token, config.JWT_VERIFICATION_SECRET_KEY, algorithms=[config.ALGORITHM]
|
145 |
+
)
|
146 |
+
token_data = TokenPayload(**payload)
|
147 |
+
if datetime.fromtimestamp(token_data.exp)< datetime.now():
|
148 |
+
return app.state.templates.TemplateResponse("verification_failure.html", context={"request": request})
|
149 |
+
|
150 |
+
username, email = token_data.sub.split(' ', maxsplit=1)
|
151 |
+
registered_email = DBQueries.fetch_data_from_database('auth', ['is_verified'], f"username='{username}'")
|
152 |
+
registered_email = list(registered_email)
|
153 |
+
if len(registered_email) == 0:
|
154 |
+
raise InfoNotFoundException(response_result,"User not found")
|
155 |
+
print(registered_email[0][0])
|
156 |
+
if registered_email[0][0]:
|
157 |
+
return app.state.templates.TemplateResponse("verification_failure.html", context={"request": request})
|
158 |
+
|
159 |
+
|
160 |
+
DBQueries.update_data_in_database('auth','is_verified',f"username='{username}'", (True,))
|
161 |
+
DBQueries.update_data_in_database('auth','email',f"username='{username}'", email)
|
162 |
+
response_result.status = 'success'
|
163 |
+
response_result.message = [f'Email verified successfully']
|
164 |
+
return app.state.templates.TemplateResponse("verification_success.html", context={"request": request})
|
165 |
+
|
166 |
+
except (jwt.JWTError, ValidationError):
|
167 |
+
return app.state.templates.TemplateResponse("verification_failure.html", context={"request": request})
|
168 |
+
|
TechdocsAPI/backend/services/auth/utils/auth_funcs.py
CHANGED
@@ -16,9 +16,11 @@ from backend.core.Exceptions import *
|
|
16 |
from backend.models import TokenPayload, TokenSchema
|
17 |
|
18 |
|
19 |
-
|
20 |
-
ACCESS_TOKEN_EXPIRE_MINUTES
|
21 |
-
REFRESH_TOKEN_EXPIRE_MINUTES
|
|
|
|
|
22 |
|
23 |
|
24 |
class Auth:
|
@@ -73,7 +75,7 @@ class Auth:
|
|
73 |
return entered_username == db_username
|
74 |
|
75 |
@staticmethod
|
76 |
-
def create_access_token(subject: Union[str, Any], expires_delta: int = None) -> str:
|
77 |
"""Creates JWT access token.
|
78 |
|
79 |
Args:
|
@@ -83,33 +85,20 @@ class Auth:
|
|
83 |
Returns:
|
84 |
encoded_jwt: str. Encoded JWT token from the subject of interest.
|
85 |
"""
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
encoded_jwt = jwt.encode(to_encode, config.JWT_SECRET_KEY, config.ALGORITHM)
|
93 |
-
return encoded_jwt
|
94 |
-
|
95 |
-
@staticmethod
|
96 |
-
def create_refresh_token(subject: Union[str, Any], expires_delta: int = None) -> str:
|
97 |
-
"""Creates JWT refresh access token.
|
98 |
-
|
99 |
-
Args:
|
100 |
-
subject: Union[Any, str]. Hash_key to generate access token from.
|
101 |
-
expires_delta: int = None. Expiry time for the JWT.
|
102 |
|
103 |
-
Returns:
|
104 |
-
encoded_jwt: str. Encoded JWT token from the subject of interest.
|
105 |
-
"""
|
106 |
if expires_delta is not None:
|
107 |
expires_delta = datetime.utcnow() + expires_delta
|
108 |
else:
|
109 |
-
expires_delta = datetime.utcnow() + timedelta(minutes=
|
110 |
-
|
111 |
to_encode = {"exp": expires_delta, "sub": str(subject)}
|
112 |
-
encoded_jwt = jwt.encode(to_encode,
|
113 |
return encoded_jwt
|
114 |
|
115 |
@staticmethod
|
@@ -141,9 +130,7 @@ class Auth:
|
|
141 |
except (jwt.JWTError, ValidationError):
|
142 |
raise InvalidCredentialsException(tokens)
|
143 |
tokens['access_token'] = Auth.create_access_token(token_data.sub)
|
144 |
-
tokens['refresh_token'] = Auth.
|
145 |
-
tokens['status'] = 'login successful'
|
146 |
-
tokens['role'] = token_data.sub.split("_")[1]
|
147 |
return tokens
|
148 |
|
149 |
@classmethod
|
|
|
16 |
from backend.models import TokenPayload, TokenSchema
|
17 |
|
18 |
|
19 |
+
token_expiry_info = {
|
20 |
+
'ACCESS_TOKEN_EXPIRE_MINUTES': 30, # 30 minutes
|
21 |
+
'REFRESH_TOKEN_EXPIRE_MINUTES': 60 * 24 * 3, # 3 days
|
22 |
+
'VERIFICATION_TOKEN_EXPIRE_MINUTES': 20, # 20 minutes
|
23 |
+
}
|
24 |
|
25 |
|
26 |
class Auth:
|
|
|
75 |
return entered_username == db_username
|
76 |
|
77 |
@staticmethod
|
78 |
+
def create_access_token(subject: Union[str, Any], expires_delta: int = None, secret_name: str = None) -> str:
|
79 |
"""Creates JWT access token.
|
80 |
|
81 |
Args:
|
|
|
85 |
Returns:
|
86 |
encoded_jwt: str. Encoded JWT token from the subject of interest.
|
87 |
"""
|
88 |
+
secret_key = config.JWT_SECRET_KEY
|
89 |
+
token_expiration = token_expiry_info['ACCESS_TOKEN_EXPIRE_MINUTES']
|
90 |
+
|
91 |
+
if secret_name is not None:
|
92 |
+
secret_key = config.dict()["JWT_" + secret_name.upper() + "_SECRET_KEY"]
|
93 |
+
token_expiration = token_expiry_info[secret_name.upper() + "_TOKEN_EXPIRE_MINUTES"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
|
|
|
|
|
|
|
95 |
if expires_delta is not None:
|
96 |
expires_delta = datetime.utcnow() + expires_delta
|
97 |
else:
|
98 |
+
expires_delta = datetime.utcnow() + timedelta(minutes=token_expiration)
|
99 |
+
|
100 |
to_encode = {"exp": expires_delta, "sub": str(subject)}
|
101 |
+
encoded_jwt = jwt.encode(to_encode, secret_key, config.ALGORITHM)
|
102 |
return encoded_jwt
|
103 |
|
104 |
@staticmethod
|
|
|
130 |
except (jwt.JWTError, ValidationError):
|
131 |
raise InvalidCredentialsException(tokens)
|
132 |
tokens['access_token'] = Auth.create_access_token(token_data.sub)
|
133 |
+
tokens['refresh_token'] = Auth.create_access_token(token_data.sub, secret_name='REFRESH')
|
|
|
|
|
134 |
return tokens
|
135 |
|
136 |
@classmethod
|
TechdocsAPI/backend/templates/email_verification.html
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>Verify Email</title>
|
5 |
+
<style>
|
6 |
+
body {
|
7 |
+
font-family: Arial, sans-serif;
|
8 |
+
background-color: #f4f4f4;
|
9 |
+
margin: 0;
|
10 |
+
padding: 0;
|
11 |
+
}
|
12 |
+
|
13 |
+
.container {
|
14 |
+
width: 80%;
|
15 |
+
margin: 0 auto;
|
16 |
+
padding: 20px;
|
17 |
+
background-color: #fff;
|
18 |
+
border-radius: 8px;
|
19 |
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
20 |
+
}
|
21 |
+
|
22 |
+
h3 {
|
23 |
+
color: #333;
|
24 |
+
text-align: center;
|
25 |
+
}
|
26 |
+
|
27 |
+
p {
|
28 |
+
color: #555;
|
29 |
+
text-align: center;
|
30 |
+
margin-bottom: 20px;
|
31 |
+
}
|
32 |
+
|
33 |
+
a.button {
|
34 |
+
display: block;
|
35 |
+
width: 50%;
|
36 |
+
margin: 20px auto;
|
37 |
+
padding: 12px;
|
38 |
+
border-radius: 5px;
|
39 |
+
text-align: center;
|
40 |
+
text-decoration: none;
|
41 |
+
background-color: #0275d8;
|
42 |
+
color: #fff;
|
43 |
+
font-size: 1rem;
|
44 |
+
transition: background-color 0.3s;
|
45 |
+
}
|
46 |
+
|
47 |
+
a.button:hover {
|
48 |
+
background-color: #025aa5;
|
49 |
+
}
|
50 |
+
|
51 |
+
.disclaimer {
|
52 |
+
color: #777;
|
53 |
+
text-align: center;
|
54 |
+
}
|
55 |
+
</style>
|
56 |
+
</head>
|
57 |
+
<body>
|
58 |
+
<div class="container">
|
59 |
+
<h3>Account Verification</h3>
|
60 |
+
|
61 |
+
<p>Hi, {{username}}👋. Thank you for registering with Techdocs. Please click the button below to verify your account:</p>
|
62 |
+
|
63 |
+
<a class="button" href="{{verify_link}}" target="_blank">Verify your email</a>
|
64 |
+
|
65 |
+
<p class="disclaimer">Please disregard this email if you did not register with Techdocs. Thank you.</p>
|
66 |
+
</div>
|
67 |
+
</body>
|
68 |
+
</html>
|
TechdocsAPI/backend/templates/verification_failure.html
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
8 |
+
<title>Techdocs</title>
|
9 |
+
</head>
|
10 |
+
<body>
|
11 |
+
|
12 |
+
<div class="container">
|
13 |
+
<div class="row" style="margin-top: 30vh;">
|
14 |
+
<div class="col-md-10 col-sm-10 col-xm-12 m-auto p-4">
|
15 |
+
<div class="card text-center">
|
16 |
+
<div class="card-header">
|
17 |
+
Email Verification
|
18 |
+
</div>
|
19 |
+
<div class="card-body">
|
20 |
+
<h5 class="card-title">Already Verified/Link Expired</h5>
|
21 |
+
<p class="card-text">Something went wrong</p>
|
22 |
+
<button class="btn btn-secondary"><a href="https://techdocs.streamlit.app" target="_blank" style="color: white; text-decoration: none;">Login</a></button>
|
23 |
+
</div>
|
24 |
+
</div>
|
25 |
+
</div>
|
26 |
+
</div>
|
27 |
+
</div>
|
28 |
+
|
29 |
+
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
30 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
31 |
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
32 |
+
</body>
|
33 |
+
</html>
|
TechdocsAPI/backend/templates/verification_success.html
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
8 |
+
<title>Techdocs</title>
|
9 |
+
</head>
|
10 |
+
<body>
|
11 |
+
|
12 |
+
<div class="container">
|
13 |
+
<div class="row" style="margin-top: 30vh;">
|
14 |
+
<div class="col-md-10 col-sm-10 col-xm-12 m-auto p-4">
|
15 |
+
<div class="card text-center">
|
16 |
+
<div class="card-header">
|
17 |
+
Email Verification
|
18 |
+
</div>
|
19 |
+
<div class="card-body">
|
20 |
+
<h5 class="card-title">Email Verification Successful</h5>
|
21 |
+
<p class="card-text">You can now login into you account or use or services</p>
|
22 |
+
<button class="btn btn-primary"><a href="https://techdocs.streamlit.app" target="_blank" style="color: white; text-decoration: none;">Login</a></button>
|
23 |
+
</div>
|
24 |
+
</div>
|
25 |
+
</div>
|
26 |
+
</div>
|
27 |
+
</div>
|
28 |
+
|
29 |
+
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
30 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
31 |
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
32 |
+
</body>
|
33 |
+
</html>
|
TechdocsAPI/requirements.txt
CHANGED
@@ -9,3 +9,4 @@ pydantic[email]
|
|
9 |
langchain
|
10 |
clarifai
|
11 |
Pillow
|
|
|
|
9 |
langchain
|
10 |
clarifai
|
11 |
Pillow
|
12 |
+
fastapi_mail==1.3.1
|
scripts/test.py
CHANGED
@@ -31,19 +31,20 @@ else:
|
|
31 |
cursor.execute("DROP TABLE IF EXISTS auth")
|
32 |
cursor.execute("CREATE TABLE IF NOT EXISTS auth(username VARCHAR(15) PRIMARY KEY, password TEXT, email VARCHAR(50))")
|
33 |
cursor.execute("CREATE TABLE IF NOT EXISTS api_key(username VARCHAR(15),apikey TEXT, FOREIGN KEY (username) REFERENCES auth(username))")
|
|
|
34 |
|
35 |
-
QUERY = ('INSERT INTO {coll_name} '
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
|
40 |
-
testlist=[("test2","test2","[email protected]"),("test1","test1","[email protected]")]
|
41 |
-
cursor.executemany(QUERY, testlist)
|
42 |
|
43 |
-
QUERY = ('SELECT {cols} FROM {table_name} WHERE email="[email protected]"').format(cols="*", table_name="auth")
|
44 |
-
cursor.execute(QUERY)
|
45 |
-
for i in cursor.fetchall():
|
46 |
-
|
47 |
|
48 |
|
49 |
|
|
|
31 |
cursor.execute("DROP TABLE IF EXISTS auth")
|
32 |
cursor.execute("CREATE TABLE IF NOT EXISTS auth(username VARCHAR(15) PRIMARY KEY, password TEXT, email VARCHAR(50))")
|
33 |
cursor.execute("CREATE TABLE IF NOT EXISTS api_key(username VARCHAR(15),apikey TEXT, FOREIGN KEY (username) REFERENCES auth(username))")
|
34 |
+
cursor.execute("ALTER TABLE auth ADD is_verified BOOLEAN NOT NULL DEFAULT(false)")
|
35 |
|
36 |
+
# QUERY = ('INSERT INTO {coll_name} '
|
37 |
+
# '(username, password, email) '
|
38 |
+
# 'VALUES '
|
39 |
+
# '(%s, %s, %s)').format(coll_name="auth")
|
40 |
|
41 |
+
# testlist=[("test2","test2","[email protected]"),("test1","test1","[email protected]")]
|
42 |
+
# cursor.executemany(QUERY, testlist)
|
43 |
|
44 |
+
# QUERY = ('SELECT {cols} FROM {table_name} WHERE email="[email protected]"').format(cols="*", table_name="auth")
|
45 |
+
# cursor.execute(QUERY)
|
46 |
+
# for i in cursor.fetchall():
|
47 |
+
# print(i)
|
48 |
|
49 |
|
50 |
|