Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CLIENT_ORIGINS=https://fake-origin.example.com
CLIENT_ORIGINS_REGEX="^http://fake-localhost:.*"

ENV=development
##### MISTRAL AZURE #####
AZURE_MISTRAL_SWEDEN_API_BASE=https://fake-mistral-sweden.example.com/models
AZURE_MISTRAL_SWEDEN_API_KEY=FAKE_MISTRAL_SWEDEN_API_KEY
Expand Down
1 change: 1 addition & 0 deletions k8s/welearn-api/values.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ config:
PG_HOST: dev-lab-projects-backend.postgres.database.azure.com
TIKA_URL_BASE: https://tika.k8s.lp-i.dev/
DATA_COLLECTION_ORIGIN_PREFIX: welearn
ENV: development
allowedHostsRegexes:
mainUrl: |-
https:\/\/welearn\.k8s\.lp-i\.dev
Expand Down
1 change: 1 addition & 0 deletions k8s/welearn-api/values.prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ config:
PG_HOST: prod-prod-projects-backend.postgres.database.azure.com
TIKA_URL_BASE: https://tika.k8s.lp-i.org/
DATA_COLLECTION_ORIGIN_PREFIX: workshop
ENV: production
allowedHostsRegexes:
alphaUrls: |-
https://[a-zA-Z0-9-]*\.alpha-welearn\.lp-i\.org
Expand Down
1 change: 1 addition & 0 deletions k8s/welearn-api/values.staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ config:
PG_DATABASE: welearn_datastack_staging
TIKA_URL_BASE: https://tika.k8s.lp-i.dev/
DATA_COLLECTION_ORIGIN_PREFIX: welearn
ENV: staging
allowedHostsRegexes:
mainUrl: |-
https:\/\/welearn\.k8s\.lp-i\.xyz
Expand Down
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ env =
TIKA_URL_BASE=https://tika.example.com
USE_CACHED_SETTINGS=True
DATA_COLLECTION_ORIGIN_PREFIX=workshop
ENV=test

filterwarnings =
ignore:.*U.*mode is deprecated:DeprecationWarning
5 changes: 3 additions & 2 deletions src/app/api/api_v1/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

from fastapi import APIRouter

from src.app.api.api_v1.endpoints import chat, metric, micro_learning, user
from src.app.api.api_v1.endpoints import chat, metric, micro_learning
from src.app.search.api import router as search_router
from src.app.tutor.api import router as tutor_router
from src.app.user.api import router as user_router

api_router = APIRouter()
api_router.include_router(chat.router, prefix="/qna", tags=["qna"])
Expand All @@ -14,7 +15,7 @@
api_router.include_router(
micro_learning.router, prefix="/micro_learning", tags=["micro_learning"]
)
api_router.include_router(user.router, prefix="/user", tags=["user"])
api_router.include_router(user_router.router, prefix="/user", tags=["user"])


api_tags_metadata = [
Expand Down
5 changes: 3 additions & 2 deletions src/app/api/api_v1/endpoints/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
)
from src.app.shared.infra.abst_chat import get_chat_service
from src.app.shared.utils.dependencies import get_settings
from src.app.shared.utils.requests import extract_session_cookie
from src.app.utils.logger import logger as utils_logger

logger = utils_logger(__name__)
Expand Down Expand Up @@ -256,7 +257,7 @@ async def q_and_a_ans(
str: openai chat completion content
"""

session_id = request.headers.get("X-Session-ID")
session_id = extract_session_cookie(request)

try:
content = await chatfactory.chat_message(
Expand Down Expand Up @@ -354,7 +355,7 @@ async def agent_response(
data_collection=Depends(get_data_collection_service),
) -> Optional[Dict]:
try:
session_id = request.headers.get("X-Session-ID")
session_id = extract_session_cookie(request)
docs = []

if body.query is None:
Expand Down
3 changes: 2 additions & 1 deletion src/app/api/api_v1/endpoints/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from src.app.services.data_collection import get_data_collection_service
from src.app.services.sql_db.queries import get_document_qty_table_info_sync
from src.app.shared.utils.dependencies import get_settings
from src.app.shared.utils.requests import extract_session_cookie
from src.app.utils.logger import logger as utils_logger

logger = utils_logger(__name__)
Expand Down Expand Up @@ -55,6 +56,6 @@ async def update_clicked_doc_from_chat_message(
async def register_syllabus_download(
request: Request, data_collection=Depends(get_data_collection_service)
) -> str:
session_id = request.headers.get("X-Session-ID")
session_id = extract_session_cookie(request)
await data_collection.register_syllabus_download(session_id)
return "registered"
105 changes: 0 additions & 105 deletions src/app/api/api_v1/endpoints/user.py

This file was deleted.

1 change: 1 addition & 0 deletions src/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def get_api_version(self) -> dict:
AZURE_API_VERSION: str

LLM_MODEL_NAME: str
ENV: str
Comment thread
sandragjacinto marked this conversation as resolved.

# PG
PG_USER: Optional[str] = None
Expand Down
5 changes: 3 additions & 2 deletions src/app/middleware/monitor_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from starlette.middleware.base import BaseHTTPMiddleware

from src.app.services.sql_db.queries import register_endpoint
from src.app.shared.utils.requests import extract_session_cookie
from src.app.utils.logger import logger as logger_utils

logger = logger_utils(__name__)
Expand All @@ -11,7 +12,7 @@
class MonitorRequestsMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
if request.url.path.startswith("/api/v1/"):
session_id = request.headers.get("X-Session-ID")
session_id = extract_session_cookie(request)
if session_id:
try:
await run_in_threadpool(
Expand All @@ -24,7 +25,7 @@ async def dispatch(self, request: Request, call_next):
logger.error(f"Failed to register endpoint {request.url.path}: {e}")
else:
logger.warning(
f"No X-Session-ID header provided for {request.url.path}"
f"No X-Session-ID cookie provided for {request.url.path}"
Comment thread
sandragjacinto marked this conversation as resolved.
)

response = await call_next(request)
Expand Down
3 changes: 2 additions & 1 deletion src/app/search/api/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
ModelNotFoundError,
bad_request,
)
from src.app.shared.utils.requests import extract_session_cookie
from src.app.utils.logger import logger as logger_utils

router = APIRouter()
Expand Down Expand Up @@ -207,7 +208,7 @@ async def search_all(
data_collection=Depends(get_data_collection_service),
):
try:
session_id = request.headers.get("X-Session-ID")
session_id = extract_session_cookie(request)

res = await sp.search_handler(
qp=qp, method=SearchMethods.BY_DOCUMENT, background_tasks=background_tasks
Expand Down
12 changes: 4 additions & 8 deletions src/app/services/data_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def get_campaign_state(

async def register_search_data(
self,
session_id: str | None,
session_id: uuid.UUID | None,
query: str,
search_results: list[Document | ScoredPoint],
sdg_filter: list[int] | None = None,
Expand All @@ -88,9 +88,7 @@ async def register_search_data(
},
)

user_id = await run_in_threadpool(
get_user_from_session_id, uuid.UUID(session_id)
)
user_id = await run_in_threadpool(get_user_from_session_id, session_id)

if not user_id:
raise HTTPException(
Expand Down Expand Up @@ -241,7 +239,7 @@ async def register_syllabus_update(

async def register_chat_data(
self,
session_id: str | None,
session_id: uuid.UUID | None,
user_query: str,
conversation_id: uuid.UUID | None,
answer_content: str,
Expand All @@ -264,9 +262,7 @@ async def register_chat_data(
},
)

user_id = await run_in_threadpool(
get_user_from_session_id, uuid.UUID(session_id)
)
user_id = await run_in_threadpool(get_user_from_session_id, session_id)

if not user_id:
raise HTTPException(
Expand Down
20 changes: 18 additions & 2 deletions src/app/services/sql_db/queries_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from welearn_database.data.models import Bookmark, InferredUser, Session

from src.app.services.sql_db.sql_service import session_maker
from src.app.shared.domain.exceptions import SessionNotFoundError, UserNotFoundError
from src.app.utils.logger import logger as logger_utils

logger = logger_utils(__name__)
Expand All @@ -27,6 +28,8 @@ def get_or_create_user_sync(
).first()
if user:
return user.id

logger.info("Creating new user with referer: %s", referer)
user = InferredUser(origin_referrer=referer)
s.add(user)
s.commit()
Expand All @@ -45,7 +48,7 @@ def get_or_create_session_sync(
select(InferredUser.id).where(InferredUser.id == user_id)
).first()
if not user:
raise ValueError(f"User {user_id} does not exist")
raise UserNotFoundError(f"User {user_id} does not exist")

if session_id:
session = s.execute(
Expand All @@ -58,6 +61,9 @@ def get_or_create_session_sync(
if session:
return session.id

logger.info(
"Creating new session for user_id=%s with referer: %s", user_id, referer
)
new_session = Session(
inferred_user_id=user_id,
origin_referrer=referer,
Expand All @@ -70,11 +76,21 @@ def get_or_create_session_sync(
return new_session.id


def get_user_from_session_id(session_id: uuid.UUID) -> uuid.UUID | None:
def get_user_from_session_id(session_id: uuid.UUID | None) -> uuid.UUID | None:
if not session_id:
return None

with session_maker() as s:
session = s.execute(select(Session).where(Session.id == session_id)).first()
if session:
logger.info(
"Valid session. user_id=%s session_id=%s",
session[0].inferred_user_id,
session_id,
)
return session[0].inferred_user_id
Comment on lines 83 to 91
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_user_from_session_id currently treats any session row as valid and does not check Session.end_at > now (unlike get_or_create_session_sync). This can make expired sessions resolve to a user indefinitely, especially problematic now that cookies can persist long-term. Consider filtering by end_at in the query (and/or deleting/invalidating expired sessions).

Copilot uses AI. Check for mistakes.
else:
raise SessionNotFoundError(f"Session with id {session_id} not found")
return None


Expand Down
4 changes: 4 additions & 0 deletions src/app/shared/domain/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@
}

APP_NAME = "welearn-api"


SESSION_COOKIE_NAME = "x-session-id"
SESSION_TTL_SECONDS = 60 * 60 * 24 * 400 # 400 days
Comment thread
sandragjacinto marked this conversation as resolved.
Loading
Loading