mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-10 16:28:00 -05:00
Removed unused access_tokens table from DB
[backend] Updated models to remove unused access_tokens table [backend] Removed unused access_tokens table from DB using alembic [backend] Removed unused methods and DB crud logic for access_tokens table [backend] Removed scheduler to remove expired access tokens from the DB
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
"""Remove access_tokens table
|
||||
|
||||
Revision ID: 0ab200a7f196
|
||||
Revises: 5fd61bc55e09
|
||||
Create Date: 2024-05-24 13:39:50.917676
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '0ab200a7f196'
|
||||
down_revision: Union[str, None] = '5fd61bc55e09'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
# Drop the foreign key constraint first
|
||||
op.drop_constraint('access_tokens_ibfk_1', 'access_tokens', type_='foreignkey')
|
||||
# Then drop the index
|
||||
op.drop_index('ix_access_tokens_user_id', table_name='access_tokens')
|
||||
op.drop_table('access_tokens')
|
||||
op.alter_column('users_integrations', 'user_id',
|
||||
existing_type=mysql.INTEGER(display_width=11),
|
||||
comment='User ID that the integration belongs',
|
||||
existing_comment='User ID that the token belongs',
|
||||
existing_nullable=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('users_integrations', 'user_id',
|
||||
existing_type=mysql.INTEGER(display_width=11),
|
||||
comment='User ID that the token belongs',
|
||||
existing_comment='User ID that the integration belongs',
|
||||
existing_nullable=False)
|
||||
op.create_table('access_tokens',
|
||||
sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False),
|
||||
sa.Column('token', mysql.VARCHAR(length=256), nullable=False, comment='User token'),
|
||||
sa.Column('user_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=False, comment='User ID that the token belongs'),
|
||||
sa.Column('created_at', mysql.DATETIME(), nullable=False, comment='Token creation date (date)'),
|
||||
sa.Column('expires_at', mysql.DATETIME(), nullable=False, comment='Token expiration date (date)'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='access_tokens_ibfk_1', ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_collate='utf8mb4_general_ci',
|
||||
mysql_default_charset='utf8mb4',
|
||||
mysql_engine='InnoDB'
|
||||
)
|
||||
# Recreate the index first
|
||||
op.create_index('ix_access_tokens_user_id', 'access_tokens', ['user_id'])
|
||||
# Then recreate the foreign key constraint
|
||||
op.create_foreign_key('access_tokens_ibfk_1', 'access_tokens', 'users', ['user_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,129 +0,0 @@
|
||||
import logging
|
||||
|
||||
import models
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
# Define a loggger created on main.py
|
||||
logger = logging.getLogger("myLogger")
|
||||
|
||||
|
||||
def get_acess_tokens_by_user_id(user_id: int, db: Session):
|
||||
try:
|
||||
access_tokens = (
|
||||
db.query(models.AccessToken)
|
||||
.filter(models.AccessToken.user_id == user_id)
|
||||
.all()
|
||||
)
|
||||
if access_tokens is None:
|
||||
# If the user was not found, return a 404 Not Found error
|
||||
return None
|
||||
|
||||
return access_tokens
|
||||
except Exception as err:
|
||||
# Log the exception
|
||||
logger.error(f"Error in get_acess_tokens_by_user_id: {err}", exc_info=True)
|
||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Internal Server Error",
|
||||
) from err
|
||||
|
||||
|
||||
def create_access_token(token, db: Session):
|
||||
try:
|
||||
# Create a new access token in the database
|
||||
db_access_token = models.AccessToken(
|
||||
token=token.token,
|
||||
user_id=token.user_id,
|
||||
created_at=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S"),
|
||||
expires_at=token.expires_at,
|
||||
)
|
||||
|
||||
# Add the access token to the database and commit the transaction
|
||||
db.add(db_access_token)
|
||||
db.commit()
|
||||
db.refresh(db_access_token)
|
||||
|
||||
# return the access token
|
||||
return db_access_token
|
||||
except Exception as err:
|
||||
# Handle database-related exceptions
|
||||
db.rollback() # Rollback the transaction to maintain database consistency
|
||||
|
||||
# Log the exception
|
||||
logger.error(f"Error in create_access_token: {err}", exc_info=True)
|
||||
|
||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Internal Server Error",
|
||||
) from err
|
||||
|
||||
|
||||
def delete_access_token(token: str, db: Session):
|
||||
try:
|
||||
# Delete the access token from the database
|
||||
db_access_token = (
|
||||
db.query(models.AccessToken)
|
||||
.filter(models.AccessToken.token == token)
|
||||
.delete()
|
||||
)
|
||||
|
||||
# Commit the transaction to the database
|
||||
if db_access_token:
|
||||
db.delete(db_access_token)
|
||||
db.commit()
|
||||
logger.info(f"{db_access_token} access tokens deleted from the database")
|
||||
return db_access_token
|
||||
else:
|
||||
# If the access token was not found, return a 404 Not Found error
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Access token not found",
|
||||
)
|
||||
except Exception as err:
|
||||
# Handle database-related exceptions
|
||||
db.rollback() # Rollback the transaction to maintain database consistency
|
||||
|
||||
# Log the exception
|
||||
logger.error(f"Error in delete_access_token: {err}", exc_info=True)
|
||||
|
||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Internal Server Error",
|
||||
) from err
|
||||
|
||||
|
||||
def delete_access_tokens(expiration_time: str, db: Session):
|
||||
try:
|
||||
# Delete the access tokens from the database
|
||||
db_access_tokens = (
|
||||
db.query(models.AccessToken)
|
||||
.filter(models.AccessToken.created_at < expiration_time)
|
||||
.delete()
|
||||
)
|
||||
|
||||
# Commit the transaction to the database
|
||||
if db_access_tokens:
|
||||
db.commit()
|
||||
return db_access_tokens
|
||||
else:
|
||||
# If no access tokens were found, return 0
|
||||
return 0
|
||||
except Exception as err:
|
||||
# Handle database-related exceptions
|
||||
db.rollback() # Rollback the transaction to maintain database consistency
|
||||
|
||||
# Log the exception
|
||||
logger.error(f"Error in delete_access_tokens: {err}", exc_info=True)
|
||||
|
||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Internal Server Error",
|
||||
) from err
|
||||
@@ -45,9 +45,7 @@ def startup_event():
|
||||
# Create a scheduler to run background jobs
|
||||
scheduler.start()
|
||||
|
||||
# Job to remove expired tokens every 5 minutes
|
||||
logger.info("Added scheduler job to remove expired tokens every 5 minutes")
|
||||
scheduler.add_job(remove_expired_tokens_job, "interval", minutes=5)
|
||||
# Add scheduler jobs to refresh Strava tokens and retrieve last day activities
|
||||
logger.info("Added scheduler job to refresh Strava user tokens every 60 minutes")
|
||||
scheduler.add_job(refresh_strava_tokens_job, "interval", minutes=60)
|
||||
logger.info(
|
||||
@@ -66,17 +64,6 @@ def shutdown_event():
|
||||
scheduler.shutdown()
|
||||
|
||||
|
||||
def remove_expired_tokens_job():
|
||||
# Create a new database session
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Remove expired tokens from the database
|
||||
schema_access_tokens.remove_expired_tokens(db=db)
|
||||
finally:
|
||||
# Ensure the session is closed after use
|
||||
db.close()
|
||||
|
||||
|
||||
def refresh_strava_tokens_job():
|
||||
# Create a new database session
|
||||
db = SessionLocal()
|
||||
|
||||
@@ -103,12 +103,6 @@ class User(Base):
|
||||
back_populates="user",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
# Define a relationship to AccessToken model
|
||||
access_tokens = relationship(
|
||||
"AccessToken",
|
||||
back_populates="user",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
# Define a relationship to Gear model
|
||||
gear = relationship(
|
||||
"Gear",
|
||||
@@ -147,7 +141,7 @@ class UserIntegrations(Base):
|
||||
ForeignKey("users.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="User ID that the token belongs",
|
||||
comment="User ID that the integration belongs",
|
||||
)
|
||||
strava_state = Column(String(length=45), default=None, nullable=True)
|
||||
strava_token = Column(String(length=250), default=None, nullable=True)
|
||||
@@ -164,28 +158,6 @@ class UserIntegrations(Base):
|
||||
user = relationship("User", back_populates="users_integrations")
|
||||
|
||||
|
||||
# Data model for access_tokens table using SQLAlchemy's ORM
|
||||
class AccessToken(Base):
|
||||
__tablename__ = "access_tokens"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
token = Column(String(length=256), nullable=False, comment="User token")
|
||||
user_id = Column(
|
||||
Integer,
|
||||
ForeignKey("users.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="User ID that the token belongs",
|
||||
)
|
||||
created_at = Column(DateTime, nullable=False, comment="Token creation date (date)")
|
||||
expires_at = Column(
|
||||
DateTime, nullable=False, comment="Token expiration date (date)"
|
||||
)
|
||||
|
||||
# Define a relationship to the User model
|
||||
user = relationship("User", back_populates="access_tokens")
|
||||
|
||||
|
||||
# Data model for gear table using SQLAlchemy's ORM
|
||||
class Gear(Base):
|
||||
__tablename__ = "gear"
|
||||
|
||||
@@ -7,7 +7,6 @@ from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import JWTError, jwt
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from crud import crud_access_tokens
|
||||
from constants import (
|
||||
JWT_EXPIRATION_IN_MINUTES,
|
||||
JWT_ALGORITHM,
|
||||
@@ -69,9 +68,10 @@ def validate_token_expiration(db: Session, token: str = Depends(oauth2_scheme)):
|
||||
or datetime.utcfromtimestamp(expiration_timestamp) < datetime.utcnow()
|
||||
):
|
||||
logger.warning(
|
||||
"Token expired | Will force remove_expired_tokens to run | Returning 401 response"
|
||||
"Token expired | Returning 401 response"
|
||||
)
|
||||
remove_expired_tokens(db)
|
||||
|
||||
# Raise an HTTPException with a 401 Unauthorized status code
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Token no longer valid",
|
||||
@@ -80,10 +80,9 @@ def validate_token_expiration(db: Session, token: str = Depends(oauth2_scheme)):
|
||||
except Exception:
|
||||
# Log the error and raise the exception
|
||||
logger.info(
|
||||
"Token expired during validation | Will force remove_expired_tokens to run | Returning 401 response"
|
||||
"Token expired during validation | Returning 401 response"
|
||||
)
|
||||
# Remove expired tokens from the database
|
||||
remove_expired_tokens(db)
|
||||
|
||||
# Raise an HTTPException with a 401 Unauthorized status code
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
@@ -159,29 +158,5 @@ def create_access_token(
|
||||
# Encode the data and return the token
|
||||
encoded_jwt = jwt.encode(to_encode, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM)
|
||||
|
||||
# Save the token in the database
|
||||
db_access_token = crud_access_tokens.create_access_token(
|
||||
CreateToken(
|
||||
token=encoded_jwt,
|
||||
user_id=data.get("id"),
|
||||
expires_at=expire.strftime("%Y-%m-%dT%H:%M:%S"),
|
||||
),
|
||||
db,
|
||||
)
|
||||
if db_access_token:
|
||||
# Return the token
|
||||
return encoded_jwt
|
||||
else:
|
||||
# If the token could not be saved in the database return None
|
||||
return None
|
||||
|
||||
|
||||
def remove_expired_tokens(db: Session):
|
||||
# Calculate the expiration time
|
||||
expiration_time = datetime.utcnow() - timedelta(minutes=JWT_EXPIRATION_IN_MINUTES)
|
||||
|
||||
# Delete the expired tokens from the database
|
||||
rows_deleted = crud_access_tokens.delete_access_tokens(expiration_time, db)
|
||||
|
||||
# Log the number of tokens deleted
|
||||
logger.info(f"{rows_deleted} access tokens deleted from the database")
|
||||
# Return the token
|
||||
return encoded_jwt
|
||||
|
||||
Reference in New Issue
Block a user