Fix change user password + Strava callback

[docker] on build image process change frontend location from inside backend folder to specific frontend folder on /app
[docker] added healthcheck to image
[docker] fixed non-root user
[docker] updated docker compose example file
[docs] updated volumes
[backend] added alembic migration to change database password field size from 100 to 250 in user table
[backend] fixed issue on changing user password
[backend] add Catch-All Route for the Frontend to ensure that requests to /api/v1/docs and other API routes take precedence
This commit is contained in:
João Vitória Silva
2024-12-27 21:07:40 +00:00
parent 69a0bcd211
commit 666897a63d
8 changed files with 83 additions and 21 deletions

View File

@@ -0,0 +1,40 @@
"""v0.7.0
Revision ID: 446a199ddd37
Revises: 542605083c0c
Create Date: 2024-12-26 21:43:01.958551
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = '446a199ddd37'
down_revision: Union[str, None] = '542605083c0c'
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! ###
op.drop_constraint('activities_garminconnect_activity_id_key', 'activities', type_='unique')
op.alter_column('users', 'password',
existing_type=sa.VARCHAR(length=100),
type_=sa.VARCHAR(length=250),
existing_comment='User password (hash)',
existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('users', 'password',
existing_type=sa.VARCHAR(length=250),
type_=sa.VARCHAR(length=100),
existing_comment='User password (hash)',
existing_nullable=False)
op.create_unique_constraint('activities_garminconnect_activity_id_key', 'activities', ['garminconnect_activity_id'])
# ### end Alembic commands ###

View File

@@ -4,6 +4,7 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from fastapi import Request
from alembic.config import Config
from alembic import command
@@ -98,7 +99,14 @@ def create_app() -> FastAPI:
# Add a route to serve the user images
app.mount("/user_images", StaticFiles(directory="user_images"), name="user_images")
app.mount("/", StaticFiles(directory="frontend/dist", html=True), name="static")
app.mount(
"/", StaticFiles(directory="/app/frontend/dist", html=True), name="frontend"
)
@app.get("/{full_path:path}")
async def catch_all(full_path: str, request: Request):
# Serve the frontend's index.html for any unmatched routes
return FileResponse("/app/frontend/dist/index.html")
return app

View File

@@ -50,14 +50,18 @@ def is_password_complexity_valid(password):
def hash_password(password: str):
# Hash the password and return it
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt())
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
def verify_password(plain_password: str, hashed_password: str):
# Check if the password is equal to the hashed password
return bcrypt.checkpw(
plain_password.encode("utf-8"), hashed_password.encode("utf-8")
)
try:
return bcrypt.checkpw(
plain_password.encode("utf-8"), hashed_password.encode("utf-8")
)
except Exception as e:
logging.error(f"Error verifying password: {e}")
return False
def decode_token(token: Annotated[str, Depends(oauth2_scheme)]):

View File

@@ -35,7 +35,7 @@ class User(Base):
comment="User email (max 250 characters)",
)
password = Column(
String(length=100), nullable=False, comment="User password (hash)"
String(length=250), nullable=False, comment="User password (hash)"
)
city = Column(String(length=250), nullable=True, comment="User city")
birthdate = Column(Date, nullable=True, comment="User birthdate (date)")

View File

@@ -17,10 +17,10 @@ services:
- BEHIND_PROXY=false # default value is false. Change to true if behind reverse proxy
volumes:
# - <local_path>/endurain/backend/app:/app # Configure volume if you want to edit the code locally by cloning the repo
- <local_path>/endurain/backend/user_images:/app/user_images # necessary for user image persistence on container image updates
- <local_path>/endurain/backend/files/bulk_import:/app/files/bulk_import # necessary to enable bulk import of activities. Place here your activities files
- <local_path>/endurain/backend/files/processed:/app/files/processed # necessary for processed original files persistence on container image updates
- <local_path>/endurain/backend/logs:/app/logs # log files for the backend
- <local_path>/endurain/backend/user_images:/app/backend/user_images # necessary for user image persistence on container image updates
- <local_path>/endurain/backend/files/bulk_import:/app/backend/files/bulk_import # necessary to enable bulk import of activities. Place here your activities files
- <local_path>/endurain/backend/files/processed:/app/backend/files/processed # necessary for processed original files persistence on container image updates
- <local_path>/endurain/backend/logs:/app/backend/logs # log files for the backend
ports:
- "8080:80" # Endurain port, change per your needs
depends_on:

View File

@@ -55,11 +55,20 @@ ENV TZ="UTC" \
GEOCODES_MAPS_API="changeme" \
BEHIND_PROXY=false
# Set a non-root user
RUN adduser --disabled-password --gecos '' endurain
# Set the working directory to /app/frontend
WORKDIR /app/frontend
# Copy the directory app contents to /app
COPY --from=frontend-build /tmp/frontend/dist ./dist
# Set the working directory to /app/backend
WORKDIR /app/backend
# Add a non-root user
RUN useradd -m endurain
#RUN useradd -m endurain
# Copy requirements.txt from requirements-stage to /app/backend
COPY --from=requirements-stage /tmp/backend/requirements.txt ./requirements.txt
@@ -70,9 +79,6 @@ RUN pip install --no-cache-dir --upgrade -r ./requirements.txt
# Copy the directory app contents to /app
COPY backend/app ./
# Copy the directory app contents to /app
COPY --from=frontend-build /tmp/frontend/dist ./frontend/dist
# Copy the entrypoint script for starting the FastAPI app
COPY docker/start.sh /docker-entrypoint.d/start.sh
@@ -80,7 +86,8 @@ COPY docker/start.sh /docker-entrypoint.d/start.sh
RUN chmod +x /docker-entrypoint.d/start.sh
# Change ownership to non-root user
RUN chown -R endurain:endurain ./
#RUN chown -R endurain:endurain ./
RUN chown -R endurain /app
# Switch to non-root user
USER endurain
@@ -88,5 +95,8 @@ USER endurain
# Make port 80 available to the world outside this container
EXPOSE 80
# Add a healthcheck
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s CMD curl -f http://localhost/api/v1/about || exit 1
# Run the FastAPI app
ENTRYPOINT ["/docker-entrypoint.d/start.sh"]

View File

@@ -4,13 +4,13 @@ set -e
# Substitute MY_APP_ENDURAIN_HOST with the value of ENDURAIN_HOST
if [ ! -z "$ENDURAIN_HOST" ]; then
echo "Substituting MY_APP_ENDURAIN_HOST with $ENDURAIN_HOST"
find /app/backend/frontend/dist -type f \( -name '*.js' -o -name '*.css' \) -exec sed -i "s|MY_APP_ENDURAIN_HOST|${ENDURAIN_HOST}|g" '{}' +
find /app/frontend/dist -type f \( -name '*.js' -o -name '*.css' \) -exec sed -i "s|MY_APP_ENDURAIN_HOST|${ENDURAIN_HOST}|g" '{}' +
fi
# Substitute MY_APP_STRAVA_CLIENT_ID with the value of STRAVA_CLIENT_ID
if [ ! -z "$STRAVA_CLIENT_ID" ]; then
echo "Substituting MY_APP_STRAVA_CLIENT_ID with $STRAVA_CLIENT_ID"
find /app/backend/frontend/dist -type f \( -name '*.js' -o -name '*.css' \) -exec sed -i "s|MY_APP_STRAVA_CLIENT_ID|${STRAVA_CLIENT_ID}|g" '{}' +
find /app/frontend/dist -type f \( -name '*.js' -o -name '*.css' \) -exec sed -i "s|MY_APP_STRAVA_CLIENT_ID|${STRAVA_CLIENT_ID}|g" '{}' +
fi
echo "Starting FastAPI with BEHIND_PROXY=$BEHIND_PROXY"

View File

@@ -75,10 +75,10 @@ It is recommended to configure the following volumes for data persistence:
| Volume | Path | Notes |
| --- | --- | --- |
| /app/files/bulk_import | <local_path>/endurain/backend/files/bulk_import:/app/files/bulk_import | Necessary to enable bulk import of activities. Place here your activities files |
| /app/files/processed | <local_path>/endurain/backend/files/processed:/app/files/processed | Necessary for processed original files persistence on container image updates |
| /app/user_images | <local_path>/endurain/backend/user_images:/app/user_images | Necessary for user image persistence on container image updates |
| /app/logs | <local_path>/endurain/backend/logs:/app/logs | Log files for the backend |
| /app/backend/files/bulk_import | <local_path>/endurain/backend/files/bulk_import:/app/files/bulk_import | Necessary to enable bulk import of activities. Place here your activities files |
| /app/backend/files/processed | <local_path>/endurain/backend/files/processed:/app/files/processed | Necessary for processed original files persistence on container image updates |
| /app/backend/user_images | <local_path>/endurain/backend/user_images:/app/user_images | Necessary for user image persistence on container image updates |
| /app/backend/logs | <local_path>/endurain/backend/logs:/app/logs | Log files for the backend |
## Bulk import and file upload