sachin commited on
Commit
a9fdc8f
·
1 Parent(s): 89878fd

update-proxy

Browse files
.env.server DELETED
@@ -1,9 +0,0 @@
1
- PORT=7860
2
- HOST=0.0.0.0
3
- SPEECH_RATE_LIMIT=5/minute
4
- CHAT_RATE_LIMIT=100/minute
5
- EXTERNAL_TTS_URL=https://slabstech-dhwani-internal-api-server.hf.space/v1/audio/speech
6
- EXTERNAL_ASR_URL=https://gaganyatri-asr-indic-server-cpu.hf.space
7
- EXTERNAL_TEXT_GEN_URL=https://slabstech-dhwani-internal-api-server.hf.space/v1/audio/speech
8
- EXTERNAL_AUDIO_PROC_URL=https://slabstech-dhwani-internal-api-server.hf.space/v1/audio/speech
9
- API_KEY_SECRET=your_secret_key
 
 
 
 
 
 
 
 
 
 
docs/menv.md DELETED
@@ -1,9 +0,0 @@
1
- export PORT=7860
2
- export HOST=0.0.0.0
3
- export SPEECH_RATE_LIMIT=5/minute
4
- export CHAT_RATE_LIMIT=100/minute
5
- export EXTERNAL_TTS_URL=https://slabstech-dhwani-internal-api-server.hf.space/v1/audio/speech
6
- export EXTERNAL_ASR_URL=https://gaganyatri-asr-indic-server-cpu.hf.space
7
- export EXTERNAL_TEXT_GEN_URL=https://slabstech-dhwani-internal-api-server.hf.space
8
- export EXTERNAL_AUDIO_PROC_URL=https://slabstech-dhwani-internal-api-server.hf.space
9
- export API_KEY_SECRET=your_secret_key
 
 
 
 
 
 
 
 
 
 
docs/tranlate.md DELETED
@@ -1,66 +0,0 @@
1
-
2
-
3
- curl -X 'POST' \
4
- 'https://gaganyatri-translate-indic-server-cpu.hf.space/translate?src_lang=eng_Latn&tgt_lang=kan_Knda' \
5
- -H 'accept: application/json' \
6
- -H 'Content-Type: application/json' \
7
- -d '{
8
- "sentences": [
9
- "Hello, how are you?", "Good morning!"
10
- ],
11
- "src_lang": "eng_Latn",
12
- "tgt_lang": "kan_Knda"
13
- }'
14
-
15
-
16
- {
17
- "translations": [
18
- "ಹಲೋ, ಹೇಗಿದ್ದೀರಿ? ",
19
- "ಶುಭೋದಯ! "
20
- ]
21
- }
22
-
23
-
24
-
25
-
26
- curl -X 'POST' \
27
- 'https://gaganyatri-translate-indic-server-cpu.hf.space/translate?src_lang=kan_Knda&tgt_lang=eng_Latn' \
28
- -H 'accept: application/json' \
29
- -H 'Content-Type: application/json' \
30
- -d '{
31
- "sentences": [
32
- "ನಮಸ್ಕಾರ, ಹೇಗಿದ್ದೀರಾ?", "ಶುಭೋದಯ!"
33
- ],
34
- "src_lang": "kan_Knda",
35
- "tgt_lang": "eng_Latn"
36
- }'
37
-
38
-
39
- {
40
- "translations": [
41
- "Hello, how are you?",
42
- "Good morning!"
43
- ]
44
- }
45
-
46
-
47
-
48
- curl -X 'POST' \
49
- 'https://gaganyatri-translate-indic-server-cpu.hf.space/translate?src_lang=kan_Knda&tgt_lang=hin_Deva' \
50
- -H 'accept: application/json' \
51
- -H 'Content-Type: application/json' \
52
- -d '{
53
- "sentences": [
54
- "ನಮಸ್ಕಾರ, ಹೇಗಿದ್ದೀರಾ?", "ಶುಭೋದಯ!"
55
- ],
56
- "src_lang": "kan_Knda",
57
- "tgt_lang": "hin_Deva"
58
- }'
59
-
60
-
61
- {
62
- "translations": [
63
- "हैलो, कैसा लग रहा है? ",
64
- "गुड मॉर्निंग! "
65
- ]
66
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,12 +1,4 @@
1
  uvicorn
2
  fastapi
3
- pydantic_settings
4
- slowapi
5
  requests
6
- python-multipart
7
- pillow
8
- pyjwt
9
- sqlalchemy
10
- passlib[bcrypt]
11
- pycryptodome
12
  httpx
 
1
  uvicorn
2
  fastapi
 
 
3
  requests
 
 
 
 
 
 
4
  httpx
src/server/config/logging_config.py DELETED
@@ -1,35 +0,0 @@
1
- import logging
2
- import logging.config
3
- from logging.handlers import RotatingFileHandler
4
- from .tts_config import config
5
-
6
- logging_config = {
7
- "version": 1,
8
- "disable_existing_loggers": False,
9
- "formatters": {
10
- "simple": {"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"},
11
- },
12
- "handlers": {
13
- "stdout": {
14
- "class": "logging.StreamHandler",
15
- "formatter": "simple",
16
- "stream": "ext://sys.stdout",
17
- },
18
- "file": {
19
- "class": "logging.handlers.RotatingFileHandler",
20
- "formatter": "simple",
21
- "filename": "dhwani_api.log",
22
- "maxBytes": 10 * 1024 * 1024, # 10MB
23
- "backupCount": 5,
24
- },
25
- },
26
- "loggers": {
27
- "root": {
28
- "level": config.log_level.upper(),
29
- "handlers": ["stdout", "file"],
30
- },
31
- },
32
- }
33
-
34
- logging.config.dictConfig(logging_config)
35
- logger = logging.getLogger("indic_all_server")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/server/config/tts_config.py DELETED
@@ -1,27 +0,0 @@
1
- import enum
2
- from pydantic_settings import BaseSettings
3
-
4
- SPEED = 1.0
5
-
6
- class StrEnum(str, enum.Enum):
7
- def __str__(self):
8
- return str(self.value)
9
-
10
- class ResponseFormat(StrEnum):
11
- MP3 = "mp3"
12
- FLAC = "flac"
13
- WAV = "wav"
14
-
15
- class Config(BaseSettings):
16
- log_level: str = "info"
17
- model: str = "ai4bharat/indic-parler-tts"
18
- max_models: int = 1
19
- lazy_load_model: bool = False # Unused now, as all models are lazy-loaded
20
- input: str = "ನಿಮ್ಮ ಇನ್‌ಪುಟ್ ಪಠ್ಯವನ್ನು ಇಲ್ಲಿ ಸೇರಿಸಿ"
21
- voice: str = (
22
- "Female speaks with a high pitch at a normal pace in a clear, close-sounding environment. "
23
- "Her neutral tone is captured with excellent audio quality."
24
- )
25
- response_format: ResponseFormat = ResponseFormat.MP3
26
-
27
- config = Config()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/server/main.py CHANGED
@@ -1,7 +1,7 @@
1
  from fastapi import FastAPI, Request, HTTPException
2
  from fastapi.responses import Response
3
  import httpx
4
-
5
  # FastAPI app setup
6
  app = FastAPI(
7
  title="Dhwani API Proxy",
@@ -11,7 +11,8 @@ app = FastAPI(
11
  )
12
 
13
  # Target server to forward requests to
14
- TARGET_SERVER = "https://dwani-dwani-server.hf.space" # Replace with the actual target server IP and port
 
15
 
16
  # Catch-all route to forward all requests
17
  @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"])
 
1
  from fastapi import FastAPI, Request, HTTPException
2
  from fastapi.responses import Response
3
  import httpx
4
+ import os
5
  # FastAPI app setup
6
  app = FastAPI(
7
  title="Dhwani API Proxy",
 
11
  )
12
 
13
  # Target server to forward requests to
14
+ TARGET_SERVER = os.getenv("DWANI_API_BASE_URL") # Replace with the actual target server IP and port
15
+
16
 
17
  # Catch-all route to forward all requests
18
  @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"])
src/server/utils/auth.py DELETED
@@ -1,323 +0,0 @@
1
- import jwt
2
- from datetime import datetime, timedelta
3
- from fastapi import HTTPException, status, Depends
4
- from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
5
- from pydantic import BaseModel, Field
6
- from pydantic_settings import BaseSettings
7
- from config.logging_config import logger
8
- from sqlalchemy import create_engine, Column, String, Boolean
9
- from sqlalchemy.ext.declarative import declarative_base
10
- from sqlalchemy.orm import sessionmaker
11
- from passlib.context import CryptContext
12
- import os
13
- import base64
14
- from Crypto.Cipher import AES
15
- from Crypto.Random import get_random_bytes
16
-
17
- # SQLite database setup with Hugging Face persistent storage
18
- DATABASE_PATH = "/data/users.db"
19
- DATABASE_URL = f"sqlite:///{DATABASE_PATH}"
20
- engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
21
- Base = declarative_base()
22
- SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
23
-
24
- # Model for admin-related users
25
- class User(Base):
26
- __tablename__ = "users"
27
- username = Column(String, primary_key=True, index=True)
28
- password = Column(String) # Stores hashed passwords
29
- is_admin = Column(Boolean, default=False)
30
- session_key = Column(String, nullable=True) # Stores base64-encoded session key
31
-
32
- # Model for app users
33
- class AppUser(Base):
34
- __tablename__ = "app_users"
35
- username = Column(String, primary_key=True, index=True)
36
- password = Column(String) # Stores hashed passwords
37
- session_key = Column(String, nullable=True) # Stores base64-encoded session key
38
-
39
- # Ensure the /data directory exists
40
- os.makedirs(os.path.dirname(DATABASE_PATH), exist_ok=True)
41
-
42
- # Create database tables
43
- Base.metadata.create_all(bind=engine)
44
-
45
- # Password hashing
46
- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
47
-
48
- class Settings(BaseSettings):
49
- api_key_secret: str = Field(..., env="API_KEY_SECRET")
50
- token_expiration_minutes: int = Field(1440, env="TOKEN_EXPIRATION_MINUTES")
51
- refresh_token_expiration_days: int = Field(7, env="REFRESH_TOKEN_EXPIRATION_DAYS")
52
- llm_model_name: str = "google/gemma-3-4b-it"
53
- max_tokens: int = 512
54
- host: str = "0.0.0.0"
55
- port: int = 7860
56
- chat_rate_limit: str = "100/minute"
57
- speech_rate_limit: str = "5/minute"
58
- external_tts_url: str = Field(..., env="EXTERNAL_TTS_URL")
59
- external_asr_url: str = Field(..., env="EXTERNAL_ASR_URL")
60
- external_text_gen_url: str = Field(..., env="EXTERNAL_TEXT_GEN_URL")
61
- external_audio_proc_url: str = Field(..., env="EXTERNAL_AUDIO_PROC_URL")
62
- external_api_base_url: str = Field("http://localhost:7860", env="EXTERNAL_API_BASE_URL") # New field
63
- external_pdf_api_base_url: str = Field("http://localhost:7861", env="EXTERNAL_PDF_API_BASE_URL") # New field
64
- default_admin_username: str = Field("admin", env="DEFAULT_ADMIN_USERNAME")
65
- default_admin_password: str = Field("admin54321", env="DEFAULT_ADMIN_PASSWORD")
66
- database_path: str = DATABASE_PATH
67
-
68
- class Config:
69
- env_file = ".env"
70
- env_file_encoding = "utf-8"
71
-
72
- settings = Settings()
73
-
74
- # Seed initial data for users table only
75
- def seed_initial_data():
76
- db = SessionLocal()
77
- try:
78
- test_username = "[email protected]"
79
- if not db.query(User).filter_by(username=test_username).first():
80
- test_device_token = "550e8400-e29b-41d4-a716-446655440000"
81
- hashed_password = pwd_context.hash(test_device_token)
82
- session_key = base64.b64encode(get_random_bytes(16)).decode('utf-8')
83
- db.add(User(username=test_username, password=hashed_password, is_admin=False, session_key=session_key))
84
- db.commit()
85
- admin_username = settings.default_admin_username
86
- admin_password = settings.default_admin_password
87
- if not db.query(User).filter_by(username=admin_username).first():
88
- hashed_password = pwd_context.hash(admin_password)
89
- session_key = base64.b64encode(get_random_bytes(16)).decode('utf-8')
90
- db.add(User(username=admin_username, password=hashed_password, is_admin=True, session_key=session_key))
91
- db.commit()
92
- logger.info(f"Seeded initial data: test user '{test_username}', admin user '{admin_username}'")
93
- except Exception as e:
94
- logger.error(f"Error seeding initial data: {str(e)}")
95
- db.rollback()
96
- finally:
97
- db.close()
98
-
99
- seed_initial_data()
100
-
101
- bearer_scheme = HTTPBearer()
102
-
103
- class TokenPayload(BaseModel):
104
- sub: str
105
- exp: float
106
- type: str
107
-
108
- class TokenResponse(BaseModel):
109
- access_token: str
110
- refresh_token: str
111
- token_type: str
112
-
113
- class LoginRequest(BaseModel):
114
- username: str
115
- password: str
116
-
117
- class RegisterRequest(BaseModel):
118
- username: str
119
- password: str
120
-
121
- def decrypt_data(encrypted_data: str, key: bytes) -> str:
122
- try:
123
- data = base64.b64decode(encrypted_data)
124
- nonce, ciphertext = data[:12], data[12:]
125
- cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
126
- plaintext = cipher.decrypt_and_verify(ciphertext[:-16], ciphertext[-16:])
127
- return plaintext.decode('utf-8')
128
- except Exception as e:
129
- logger.error(f"Decryption failed: {str(e)}")
130
- raise HTTPException(status_code=400, detail="Invalid encrypted data")
131
-
132
- async def create_access_token(user_id: str) -> dict:
133
- expire = datetime.utcnow() + timedelta(minutes=settings.token_expiration_minutes)
134
- payload = {"sub": user_id, "exp": expire.timestamp(), "type": "access"}
135
- token = jwt.encode(payload, settings.api_key_secret, algorithm="HS256")
136
- refresh_expire = datetime.utcnow() + timedelta(days=settings.refresh_token_expiration_days)
137
- refresh_payload = {"sub": user_id, "exp": refresh_expire.timestamp(), "type": "refresh"}
138
- refresh_token = jwt.encode(refresh_payload, settings.api_key_secret, algorithm="HS256")
139
- logger.info(f"Generated tokens for user: {user_id}")
140
- return {"access_token": token, "refresh_token": refresh_token}
141
-
142
- async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme)) -> str:
143
- token = credentials.credentials
144
- credentials_exception = HTTPException(
145
- status_code=status.HTTP_401_UNAUTHORIZED,
146
- detail="Invalid authentication credentials",
147
- headers={"WWW-Authenticate": "Bearer"},
148
- )
149
- try:
150
- payload = jwt.decode(token, settings.api_key_secret, algorithms=["HS256"], options={"verify_exp": False})
151
- token_data = TokenPayload(**payload)
152
- user_id = token_data.sub
153
-
154
- db = SessionLocal()
155
- # Check both users and app_users tables
156
- user = db.query(User).filter_by(username=user_id).first()
157
- app_user = db.query(AppUser).filter_by(username=user_id).first()
158
- db.close()
159
- if user_id is None or (not user and not app_user):
160
- logger.warning(f"Invalid or unknown user: {user_id}")
161
- raise credentials_exception
162
-
163
- current_time = datetime.utcnow().timestamp()
164
- if current_time > token_data.exp:
165
- logger.warning(f"Token expired: current_time={current_time}, exp={token_data.exp}")
166
- raise HTTPException(
167
- status_code=status.HTTP_401_UNAUTHORIZED,
168
- detail="Token has expired",
169
- headers={"WWW-Authenticate": "Bearer"},
170
- )
171
-
172
- logger.info(f"Validated token for user: {user_id}")
173
- return user_id
174
- except jwt.InvalidSignatureError as e:
175
- logger.error(f"Invalid signature error: {str(e)}")
176
- raise credentials_exception
177
- except jwt.InvalidTokenError as e:
178
- logger.error(f"Other token error: {str(e)}")
179
- raise credentials_exception
180
- except Exception as e:
181
- logger.error(f"Unexpected token validation error: {str(e)}")
182
- raise credentials_exception
183
-
184
- async def get_current_user_with_admin(credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme)) -> str:
185
- token = credentials.credentials
186
- credentials_exception = HTTPException(
187
- status_code=status.HTTP_401_UNAUTHORIZED,
188
- detail="Invalid authentication credentials",
189
- headers={"WWW-Authenticate": "Bearer"},
190
- )
191
- try:
192
- payload = jwt.decode(token, settings.api_key_secret, algorithms=["HS256"])
193
- token_data = TokenPayload(**payload)
194
- user_id = token_data.sub
195
-
196
- db = SessionLocal()
197
- user = db.query(User).filter_by(username=user_id).first()
198
- db.close()
199
- if not user:
200
- logger.warning(f"User not found in users table: {user_id}")
201
- raise credentials_exception
202
- if not user.is_admin:
203
- logger.warning(f"User {user_id} is not authorized as admin")
204
- raise HTTPException(
205
- status_code=status.HTTP_403_FORBIDDEN,
206
- detail="Admin access required; only admin accounts can perform this action"
207
- )
208
-
209
- logger.info(f"Validated admin user: {user_id}")
210
- return user_id
211
- except jwt.InvalidSignatureError as e:
212
- logger.error(f"Invalid signature error: {str(e)}")
213
- raise credentials_exception
214
- except jwt.InvalidTokenError as e:
215
- logger.error(f"Other token error: {str(e)}")
216
- raise credentials_exception
217
- except Exception as e:
218
- logger.error(f"Unexpected admin validation error: {str(e)}")
219
- raise credentials_exception
220
-
221
- async def login(login_request: LoginRequest, session_key_b64: str) -> TokenResponse:
222
- db = SessionLocal()
223
- session_key = base64.b64decode(session_key_b64)
224
- try:
225
- username = decrypt_data(login_request.username, session_key)
226
- password = decrypt_data(login_request.password, session_key)
227
- except:
228
- db.close()
229
- raise HTTPException(status_code=400, detail="Invalid encrypted data")
230
-
231
- # Check both users and app_users tables
232
- user = db.query(User).filter_by(username=username).first()
233
- app_user = db.query(AppUser).filter_by(username=username).first()
234
-
235
- if not user and not app_user:
236
- db.close()
237
- logger.warning(f"Login failed for user: {username}")
238
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid email or device token")
239
-
240
- target_user = user if user else app_user
241
- if not pwd_context.verify(password, target_user.password):
242
- db.close()
243
- logger.warning(f"Login failed for user: {username}")
244
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid email or device token")
245
-
246
- if target_user.session_key != session_key_b64:
247
- target_user.session_key = session_key_b64
248
- db.commit()
249
- db.close()
250
-
251
- tokens = await create_access_token(user_id=username)
252
- return TokenResponse(access_token=tokens["access_token"], refresh_token=tokens["refresh_token"], token_type="bearer")
253
-
254
- async def register(register_request: RegisterRequest, current_user: str = Depends(get_current_user_with_admin)) -> TokenResponse:
255
- db = SessionLocal()
256
- try:
257
- existing_user = db.query(User).filter_by(username=register_request.username).first()
258
- if existing_user:
259
- logger.warning(f"Registration failed: Username {register_request.username} already exists")
260
- raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Username already exists")
261
-
262
- hashed_password = pwd_context.hash(register_request.password)
263
- new_user = User(username=register_request.username, password=hashed_password, is_admin=False)
264
- db.add(new_user)
265
- db.commit()
266
- logger.info(f"Admin {current_user} successfully registered new user: {register_request.username}")
267
-
268
- tokens = await create_access_token(user_id=register_request.username)
269
- return TokenResponse(access_token=tokens["access_token"], refresh_token=tokens["refresh_token"], token_type="bearer")
270
- except Exception as e:
271
- db.rollback()
272
- logger.error(f"Registration error by admin {current_user}: {str(e)}")
273
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Registration failed: {str(e)}")
274
- finally:
275
- db.close()
276
-
277
- async def app_register(register_request: RegisterRequest, session_key_b64: str) -> TokenResponse:
278
- db = SessionLocal()
279
- session_key = base64.b64decode(session_key_b64)
280
- try:
281
- username = decrypt_data(register_request.username, session_key)
282
- password = decrypt_data(register_request.password, session_key)
283
- except:
284
- db.close()
285
- raise HTTPException(status_code=400, detail="Invalid encrypted data")
286
-
287
- # Check both tables to prevent duplicate usernames
288
- existing_user = db.query(User).filter_by(username=username).first()
289
- existing_app_user = db.query(AppUser).filter_by(username=username).first()
290
- if existing_user or existing_app_user:
291
- db.close()
292
- logger.warning(f"App registration failed: Email {username} already exists")
293
- raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered")
294
-
295
- hashed_password = pwd_context.hash(password)
296
- new_app_user = AppUser(username=username, password=hashed_password, session_key=session_key_b64)
297
- db.add(new_app_user)
298
- db.commit()
299
- db.close()
300
-
301
- tokens = await create_access_token(user_id=username)
302
- logger.info(f"App registered new user: {username}")
303
- return TokenResponse(access_token=tokens["access_token"], refresh_token=tokens["refresh_token"], token_type="bearer")
304
-
305
- async def refresh_token(credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme)) -> TokenResponse:
306
- token = credentials.credentials
307
- try:
308
- payload = jwt.decode(token, settings.api_key_secret, algorithms=["HS256"])
309
- token_data = TokenPayload(**payload)
310
- if payload.get("type") != "refresh":
311
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token type; refresh token required")
312
- user_id = token_data.sub
313
- db = SessionLocal()
314
- # Check both users and app_users tables
315
- user = db.query(User).filter_by(username=user_id).first()
316
- app_user = db.query(AppUser).filter_by(username=user_id).first()
317
- db.close()
318
- if not user and not app_user:
319
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
320
- tokens = await create_access_token(user_id=user_id)
321
- return TokenResponse(access_token=tokens["access_token"], refresh_token=tokens["refresh_token"], token_type="bearer")
322
- except jwt.InvalidTokenError:
323
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/server/utils/crypto.py DELETED
@@ -1,13 +0,0 @@
1
- from Crypto.Cipher import AES
2
- from fastapi import HTTPException
3
- from config.logging_config import logger
4
-
5
- def decrypt_data(encrypted_data: bytes, key: bytes) -> bytes:
6
- try:
7
- nonce, ciphertext = encrypted_data[:12], encrypted_data[12:]
8
- cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
9
- plaintext = cipher.decrypt_and_verify(ciphertext[:-16], ciphertext[-16:])
10
- return plaintext
11
- except Exception as e:
12
- logger.error(f"Decryption failed: {str(e)}")
13
- raise HTTPException(status_code=400, detail="Invalid encrypted data")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/server/utils/text.py DELETED
@@ -1,3 +0,0 @@
1
- def chunk_text(text: str, chunk_size: int = 15) -> list[str]:
2
- words = text.split()
3
- return [' '.join(words[i:i + chunk_size]) for i in range(0, len(words), chunk_size)]