Skip to content

Commit 07fa280

Browse files
committed
πŸ› Expose port 5013 in every deployment mode
πŸ› Minor typo fix πŸ› Minor typo fix
1 parent 2ed0f85 commit 07fa280

File tree

10 files changed

+58
-38
lines changed

10 files changed

+58
-38
lines changed

β€Žbackend/apps/northbound_app.pyβ€Ž

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import uuid
66

77
from fastapi import APIRouter, Body, Header, Request
8+
from fastapi.responses import JSONResponse
89

910
from consts.exceptions import UnauthorizedError, LimitExceededError, SignatureValidationError
1011
from services.northbound_service import (
@@ -194,8 +195,6 @@ async def update_convs_title(
194195
title=title,
195196
idempotency_key=idempotency_key,
196197
)
197-
from fastapi.responses import JSONResponse
198-
199198
headers_out = {"Idempotency-Key": result.get("idempotency_key", ""), "X-Request-Id": ctx.request_id}
200199
return JSONResponse(content=result, headers=headers_out)
201200

β€Žbackend/database/partner_db.pyβ€Ž

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ def add_mapping_id(
2727
updated_by=user_id,
2828
))
2929
session.commit()
30-
except SQLAlchemyError:
31-
return None
30+
except Exception as e:
31+
raise e
3232

3333

3434
def get_internal_id_by_external(
@@ -55,8 +55,8 @@ def get_internal_id_by_external(
5555

5656
record = query.first()
5757
return int(record.internal_id) if record and record.internal_id is not None else None
58-
except SQLAlchemyError:
59-
return None
58+
except Exception as e:
59+
raise e
6060

6161

6262
def get_external_id_by_internal(
@@ -83,6 +83,6 @@ def get_external_id_by_internal(
8383

8484
record = query.first()
8585
return str(record.external_id) if record and record.external_id is not None else None
86-
except SQLAlchemyError:
87-
return None
86+
except Exception as e:
87+
raise e
8888

β€Žbackend/services/northbound_service.pyβ€Ž

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,15 @@ def _build_idempotency_key(*parts: Any) -> str:
115115

116116

117117
# -----------------------------
118-
# ID mapping helpers (placeholders for DB-backed mapping)
118+
# ID mapping helpers
119119
# -----------------------------
120120
async def to_external_conversation_id(internal_id: int) -> str:
121121
if not internal_id:
122122
raise Exception("invalid internal conversation id")
123123
external_id = get_external_id_by_internal(internal_id=internal_id, mapping_type="CONVERSATION")
124124
if not external_id:
125-
logger.warning(f"cannot find external id for conversation_id: {internal_id}")
125+
logger.error(f"cannot find external id for conversation_id: {internal_id}")
126+
raise Exception("cannot find external id")
126127
return external_id
127128

128129

β€Žbackend/utils/auth_utils.pyβ€Ž

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
from datetime import datetime, timedelta
77
from typing import Optional, Tuple
88

9-
from fastapi import Request, HTTPException
9+
from fastapi import Request
1010
from consts.const import DEFAULT_USER_ID, DEFAULT_TENANT_ID, IS_SPEED_MODE
11-
from consts.exceptions import SignatureValidationError, UnauthorizedError
11+
from consts.exceptions import LimitExceededError, SignatureValidationError, UnauthorizedError
1212
import jwt
1313
from supabase import create_client
1414
from database.user_tenant_db import get_user_tenant_by_user_id
@@ -193,8 +193,8 @@ def validate_aksk_authentication(headers: dict, request_body: str = "") -> bool:
193193
raise SignatureValidationError("Invalid signature")
194194

195195
return True
196-
except (UnauthorizedError, SignatureValidationError):
197-
raise
196+
except (UnauthorizedError, SignatureValidationError, LimitExceededError) as e:
197+
raise e
198198
except Exception as e:
199199
logger.error(f"Unexpected error during AK/SK authentication: {e}")
200200
raise UnauthorizedError("Authentication failed")
@@ -287,7 +287,7 @@ def _extract_user_id_from_jwt_token(authorization: str) -> Optional[str]:
287287
return user_id
288288
except Exception as e:
289289
logging.error(f"Failed to extract user ID from token: {str(e)}")
290-
raise HTTPException(status_code=401, detail="Invalid or expired authentication token")
290+
raise UnauthorizedError("Invalid or expired authentication token")
291291

292292

293293
def get_current_user_id(authorization: Optional[str] = None) -> tuple[str, str]:
@@ -308,7 +308,7 @@ def get_current_user_id(authorization: Optional[str] = None) -> tuple[str, str]:
308308
try:
309309
user_id = _extract_user_id_from_jwt_token(authorization)
310310
if not user_id:
311-
raise HTTPException(status_code=401, detail="Invalid or expired authentication token")
311+
raise UnauthorizedError("Invalid or expired authentication token")
312312

313313
user_tenant_record = get_user_tenant_by_user_id(user_id)
314314
if user_tenant_record and user_tenant_record.get('tenant_id'):
@@ -322,7 +322,7 @@ def get_current_user_id(authorization: Optional[str] = None) -> tuple[str, str]:
322322

323323
except Exception as e:
324324
logging.error(f"Failed to get user ID and tanent ID: {str(e)}")
325-
raise HTTPException(status_code=401, detail="Invalid or expired authentication token")
325+
raise UnauthorizedError("Invalid or expired authentication token")
326326

327327

328328
def get_user_language(request: Request = None) -> str:

β€Ždocker/docker-compose.dev.ymlβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ services:
77
restart: always
88
ports:
99
- "5010:5010"
10+
- "5013:5013"
1011
volumes:
1112
- ../:/opt/
1213
- /opt/backend/.venv/

β€Ždocker/docker-compose.prod.ymlβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ services:
9393
max-file: "3" # Maximum number of log files to keep
9494
networks:
9595
- nexent
96+
ports:
97+
- "5013:5013" # Northbound service port
9698
entrypoint: ["/bin/bash", "-c", "python backend/nexent_mcp_service.py & python backend/northbound_service.py & python backend/main_service.py"]
9799

98100
nexent-web:

β€Ždocker/docker-compose.ymlβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ services:
8080
restart: always
8181
ports:
8282
- "5010:5010" # Backend service port
83+
- "5013:5013" # Northbound service port
8384
volumes:
8485
- ${NEXENT_USER_DIR:-$HOME/nexent}:/mnt/nexent
8586
- ${ROOT_DIR}/openssh-server/ssh-keys:/opt/ssh-keys:ro

β€Žtest/backend/app/test_northbound_app.pyβ€Ž

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from fastapi import FastAPI
66
from fastapi.responses import StreamingResponse
77
from fastapi.testclient import TestClient
8-
8+
import types
9+
import sys as _sys
910

1011
# Dynamically determine the backend path
1112
current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -16,11 +17,6 @@
1617
# Pre-mock heavy dependencies before importing router
1718
sys.modules['consts'] = MagicMock()
1819
sys.modules['consts.model'] = MagicMock()
19-
# Provide stub for consts.exceptions with expected exception classes
20-
# so that imports in application code succeed during tests.
21-
# We intentionally use real classes (not MagicMock) so that isinstance checks work if present.
22-
23-
import types
2420

2521
consts_exceptions_mod = types.ModuleType("consts.exceptions")
2622

@@ -35,8 +31,7 @@ class SignatureValidationError(Exception):
3531
consts_exceptions_mod.UnauthorizedError = UnauthorizedError
3632
consts_exceptions_mod.SignatureValidationError = SignatureValidationError
3733

38-
# Ensure the parent 'consts' is a module (could be MagicMock) and register submodule.
39-
import sys as _sys
34+
# Ensure the parent 'consts' is a module
4035
if 'consts' not in _sys.modules or not isinstance(_sys.modules['consts'], types.ModuleType):
4136
consts_root = types.ModuleType("consts")
4237
consts_root.__path__ = []

β€Žtest/backend/database/test_partner_db.pyβ€Ž

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
from unittest.mock import MagicMock
3+
import pytest
34

45
# ---------------------------------------------------------------------------
56
# Prepare stub modules and objects BEFORE importing the module under test.
@@ -118,13 +119,19 @@ def test_add_mapping_id_success(monkeypatch):
118119

119120
def test_add_mapping_id_exception(monkeypatch):
120121
# get_db_session will raise _SQLAlchemyError to simulate DB failure.
121-
def _boom():
122-
raise _SQLAlchemyError("DB down")
122+
# Simulate that entering the context raises SQLAlchemyError
123+
class _CM:
124+
def __enter__(self):
125+
raise _SQLAlchemyError("DB down")
123126

124-
monkeypatch.setattr(partner_db, 'get_db_session', _boom)
127+
def __exit__(self, exc_type, exc, tb):
128+
return False
125129

126-
# Function should catch the error and simply return None without raising.
127-
assert partner_db.add_mapping_id(1, 'e', 't', 'u') is None
130+
monkeypatch.setattr(partner_db, 'get_db_session', lambda: _CM())
131+
132+
# Function should propagate the exception; verify it raises.
133+
with pytest.raises(_SQLAlchemyError):
134+
partner_db.add_mapping_id(1, 'e', 't', 'u')
128135

129136

130137
# ---------------------------------------------------------------------------
@@ -164,11 +171,17 @@ def test_get_internal_id_by_external_not_found(monkeypatch):
164171

165172

166173
def test_get_internal_id_by_external_exception(monkeypatch):
167-
def _boom():
168-
raise _SQLAlchemyError("fail")
174+
# Prepare context manager that raises inside __enter__
175+
class _CM:
176+
def __enter__(self):
177+
raise _SQLAlchemyError("fail")
178+
179+
def __exit__(self, exc_type, exc, tb):
180+
return False
169181

170-
monkeypatch.setattr(partner_db, 'get_db_session', _boom)
171-
assert partner_db.get_internal_id_by_external('any') is None
182+
monkeypatch.setattr(partner_db, 'get_db_session', lambda: _CM())
183+
with pytest.raises(_SQLAlchemyError):
184+
partner_db.get_internal_id_by_external('any')
172185

173186

174187
# ---------------------------------------------------------------------------
@@ -206,5 +219,13 @@ def test_get_external_id_by_internal_not_found(monkeypatch):
206219

207220

208221
def test_get_external_id_by_internal_exception(monkeypatch):
209-
monkeypatch.setattr(partner_db, 'get_db_session', lambda: (_ for _ in ()).throw(_SQLAlchemyError()))
210-
assert partner_db.get_external_id_by_internal(1) is None
222+
class _CM:
223+
def __enter__(self):
224+
raise _SQLAlchemyError()
225+
226+
def __exit__(self, exc_type, exc, tb):
227+
return False
228+
229+
monkeypatch.setattr(partner_db, 'get_db_session', lambda: _CM())
230+
with pytest.raises(_SQLAlchemyError):
231+
partner_db.get_external_id_by_internal(1)

β€Žtest/backend/services/test_northbound_service.pyβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ async def test_to_external_and_internal_conversation_id_success():
158158
@pytest.mark.asyncio
159159
async def test_to_external_conversation_id_not_found():
160160
partner_db_mod.get_external_id_by_internal.return_value = None
161-
result = await ns.to_external_conversation_id(123)
162-
assert result is None
161+
with pytest.raises(Exception):
162+
await ns.to_external_conversation_id(123)
163163

164164

165165
@pytest.mark.asyncio

0 commit comments

Comments
Β (0)