sachin commited on
Commit
5754eb2
·
1 Parent(s): 8a99693
Files changed (5) hide show
  1. Dockerfile +7 -17
  2. Dockerfile.app +24 -0
  3. Dockerfile.base +18 -0
  4. src/server/main.py +12 -2
  5. src/server/utils/auth.py +30 -17
Dockerfile CHANGED
@@ -1,34 +1,24 @@
1
  # Use official Python runtime as base image
2
- FROM python:3.10-slim
3
 
4
  WORKDIR /app
5
 
6
- # Set environment variables
7
- ENV PYTHONDONTWRITEBYTECODE=1
8
- ENV PYTHONUNBUFFERED=1
9
-
10
- # Install system dependencies
11
- RUN apt-get update && apt-get install -y \
12
- gcc \
13
- && rm -rf /var/lib/apt/lists/*
14
-
15
- # Install Python dependencies
16
- COPY requirements.txt .
17
- RUN pip install --no-cache-dir -r requirements.txt
18
-
19
  # Copy application code
20
  COPY . .
21
 
 
22
  RUN useradd -ms /bin/bash appuser \
23
- && chown -R appuser:appuser /app
 
24
 
25
  USER appuser
26
- # Expose port from settings (7860 from your code)
 
27
  EXPOSE 7860
28
 
29
  # Healthcheck
30
  HEALTHCHECK --interval=30s --timeout=3s \
31
  CMD curl -f http://localhost:7860/v1/health || exit 1
32
 
33
- # Command to run the application with configurable host/port
34
  CMD ["python", "/app/src/server/main.py", "--host", "0.0.0.0", "--port", "7860"]
 
1
  # Use official Python runtime as base image
2
+ FROM slabstech/dhwani-api-server-base
3
 
4
  WORKDIR /app
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  # Copy application code
7
  COPY . .
8
 
9
+ # Create appuser and set permissions for /app and /data
10
  RUN useradd -ms /bin/bash appuser \
11
+ && mkdir -p /data \
12
+ && chown -R appuser:appuser /app /data
13
 
14
  USER appuser
15
+
16
+ # Expose port from settings
17
  EXPOSE 7860
18
 
19
  # Healthcheck
20
  HEALTHCHECK --interval=30s --timeout=3s \
21
  CMD curl -f http://localhost:7860/v1/health || exit 1
22
 
23
+ # Command to run the application
24
  CMD ["python", "/app/src/server/main.py", "--host", "0.0.0.0", "--port", "7860"]
Dockerfile.app ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use official Python runtime as base image
2
+ FROM slabstech/dhwani-api-server-base
3
+
4
+ WORKDIR /app
5
+
6
+ # Copy application code
7
+ COPY . .
8
+
9
+ # Create appuser and set permissions for /app and /data
10
+ RUN useradd -ms /bin/bash appuser \
11
+ && mkdir -p /data \
12
+ && chown -R appuser:appuser /app /data
13
+
14
+ USER appuser
15
+
16
+ # Expose port from settings
17
+ EXPOSE 7860
18
+
19
+ # Healthcheck
20
+ HEALTHCHECK --interval=30s --timeout=3s \
21
+ CMD curl -f http://localhost:7860/v1/health || exit 1
22
+
23
+ # Command to run the application
24
+ CMD ["python", "/app/src/server/main.py", "--host", "0.0.0.0", "--port", "7860"]
Dockerfile.base ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use official Python runtime as base image
2
+ FROM python:3.10-slim
3
+
4
+ WORKDIR /app
5
+
6
+ # Set environment variables
7
+ ENV PYTHONDONTWRITEBYTECODE=1
8
+ ENV PYTHONUNBUFFERED=1
9
+
10
+ # Install system dependencies
11
+ RUN apt-get update && apt-get install -y \
12
+ gcc \
13
+ curl \
14
+ && rm -rf /var/lib/apt/lists/*
15
+
16
+ # Install Python dependencies
17
+ COPY requirements.txt .
18
+ RUN pip install --no-cache-dir -r requirements.txt
src/server/main.py CHANGED
@@ -51,8 +51,16 @@ app.add_middleware(
51
  allow_headers=["*"],
52
  )
53
 
54
- # Rate limiting based on user_id
55
- limiter = Limiter(key_func=lambda request: get_current_user(request.scope.get("route").dependencies))
 
 
 
 
 
 
 
 
56
 
57
  # Request/Response Models
58
  class SpeechRequest(BaseModel):
@@ -398,11 +406,13 @@ async def transcribe_audio(
398
  response.raise_for_status()
399
 
400
  transcription = response.json().get("text", "")
 
401
  return TranscriptionResponse(text=transcription)
402
 
403
  except requests.Timeout:
404
  raise HTTPException(status_code=504, detail="Transcription service timeout")
405
  except requests.RequestException as e:
 
406
  raise HTTPException(status_code=500, detail=f"Transcription failed: {str(e)}")
407
 
408
  @app.post("/v1/chat_v2",
 
51
  allow_headers=["*"],
52
  )
53
 
54
+ # Rate limiting based on user_id with fallback to IP
55
+ async def get_user_id_for_rate_limit(request: Request):
56
+ try:
57
+ credentials = bearer_scheme(request)
58
+ user_id = await get_current_user(credentials)
59
+ return user_id
60
+ except Exception:
61
+ return get_remote_address(request) # Fallback to IP if unauthenticated
62
+
63
+ limiter = Limiter(key_func=get_user_id_for_rate_limit)
64
 
65
  # Request/Response Models
66
  class SpeechRequest(BaseModel):
 
406
  response.raise_for_status()
407
 
408
  transcription = response.json().get("text", "")
409
+ logger.info(f"Transcription completed in {time() - start_time:.2f} seconds")
410
  return TranscriptionResponse(text=transcription)
411
 
412
  except requests.Timeout:
413
  raise HTTPException(status_code=504, detail="Transcription service timeout")
414
  except requests.RequestException as e:
415
+ logger.error(f"Transcription request failed: {str(e)}")
416
  raise HTTPException(status_code=500, detail=f"Transcription failed: {str(e)}")
417
 
418
  @app.post("/v1/chat_v2",
src/server/utils/auth.py CHANGED
@@ -9,9 +9,11 @@ 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
 
13
- # SQLite database setup
14
- DATABASE_URL = "sqlite:///users.db"
 
15
  engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
16
  Base = declarative_base()
17
  SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
@@ -22,6 +24,10 @@ class User(Base):
22
  password = Column(String) # Stores hashed passwords
23
  is_admin = Column(Boolean, default=False) # New admin flag
24
 
 
 
 
 
25
  Base.metadata.create_all(bind=engine)
26
 
27
  # Password hashing
@@ -43,6 +49,7 @@ class Settings(BaseSettings):
43
  external_audio_proc_url: str = Field(..., env="EXTERNAL_AUDIO_PROC_URL")
44
  default_admin_username: str = Field("admin", env="DEFAULT_ADMIN_USERNAME")
45
  default_admin_password: str = Field("admin54321", env="DEFAULT_ADMIN_PASSWORD")
 
46
 
47
  class Config:
48
  env_file = ".env"
@@ -50,24 +57,30 @@ class Settings(BaseSettings):
50
 
51
  settings = Settings()
52
 
53
- # Seed initial data (optional)
54
  def seed_initial_data():
55
  db = SessionLocal()
56
- # Seed test user (non-admin)
57
- if not db.query(User).filter_by(username="testuser").first():
58
- hashed_password = pwd_context.hash("password123")
59
- db.add(User(username="testuser", password=hashed_password, is_admin=False))
60
- db.commit()
61
- # Seed admin user using environment variables
62
- admin_username = settings.default_admin_username
63
- admin_password = settings.default_admin_password
64
- if not db.query(User).filter_by(username=admin_username).first():
65
- hashed_password = pwd_context.hash(admin_password)
66
- db.add(User(username=admin_username, password=hashed_password, is_admin=True))
67
- db.commit()
68
- db.close()
69
- logger.info(f"Seeded initial data: admin user '{admin_username}'")
 
 
 
 
 
70
 
 
71
  seed_initial_data()
72
 
73
  # Use HTTPBearer
 
9
  from sqlalchemy.ext.declarative import declarative_base
10
  from sqlalchemy.orm import sessionmaker
11
  from passlib.context import CryptContext
12
+ import os
13
 
14
+ # SQLite database setup with Hugging Face persistent storage
15
+ DATABASE_PATH = "/data/users.db"
16
+ DATABASE_URL = f"sqlite:///{DATABASE_PATH}"
17
  engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
18
  Base = declarative_base()
19
  SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
 
24
  password = Column(String) # Stores hashed passwords
25
  is_admin = Column(Boolean, default=False) # New admin flag
26
 
27
+ # Ensure the /data directory exists
28
+ os.makedirs(os.path.dirname(DATABASE_PATH), exist_ok=True)
29
+
30
+ # Create database tables
31
  Base.metadata.create_all(bind=engine)
32
 
33
  # Password hashing
 
49
  external_audio_proc_url: str = Field(..., env="EXTERNAL_AUDIO_PROC_URL")
50
  default_admin_username: str = Field("admin", env="DEFAULT_ADMIN_USERNAME")
51
  default_admin_password: str = Field("admin54321", env="DEFAULT_ADMIN_PASSWORD")
52
+ database_path: str = DATABASE_PATH # Add for reference if needed elsewhere
53
 
54
  class Config:
55
  env_file = ".env"
 
57
 
58
  settings = Settings()
59
 
60
+ # Seed initial data
61
  def seed_initial_data():
62
  db = SessionLocal()
63
+ try:
64
+ # Seed test user (non-admin)
65
+ if not db.query(User).filter_by(username="testuser").first():
66
+ hashed_password = pwd_context.hash("password123")
67
+ db.add(User(username="testuser", password=hashed_password, is_admin=False))
68
+ db.commit()
69
+ # Seed admin user using environment variables
70
+ admin_username = settings.default_admin_username
71
+ admin_password = settings.default_admin_password
72
+ if not db.query(User).filter_by(username=admin_username).first():
73
+ hashed_password = pwd_context.hash(admin_password)
74
+ db.add(User(username=admin_username, password=hashed_password, is_admin=True))
75
+ db.commit()
76
+ logger.info(f"Seeded initial data: admin user '{admin_username}'")
77
+ except Exception as e:
78
+ logger.error(f"Error seeding initial data: {str(e)}")
79
+ db.rollback()
80
+ finally:
81
+ db.close()
82
 
83
+ # Initialize database with seed data
84
  seed_initial_data()
85
 
86
  # Use HTTPBearer