Skip to content

Commit

Permalink
remove args parsing and modify docker and ruff format
Browse files Browse the repository at this point in the history
Signed-off-by: owenowenisme <[email protected]>
  • Loading branch information
owenowenisme committed Feb 10, 2025
1 parent efcf967 commit 6a2a58e
Show file tree
Hide file tree
Showing 15 changed files with 444 additions and 704 deletions.
4 changes: 2 additions & 2 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ COPY pyproject.toml poetry.lock* /app/

# Project initialization:
RUN poetry config virtualenvs.create false \
&& poetry install --no-interaction --no-ansi
&& poetry install --no-interaction --no-ansi --no-root

# Copy project
COPY . /app

EXPOSE 8000

# Run the application
CMD ["poetry", "run", "python3", "main.py", "--mode", "prod"]
CMD ["poetry", "run", "python3", "main.py"]
3 changes: 2 additions & 1 deletion backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@

2. Run the FastAPI application:
```
poetry run python3 main.py --mode dev
source .env.dev
poetry run python3 main.py
```

The application will be available at `http://localhost:8000`.
Expand Down
2 changes: 2 additions & 0 deletions backend/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ services:
dockerfile: Dockerfile
depends_on:
- postgres
env_file:
- .env
environment:
- MODE=prod
ports:
Expand Down
21 changes: 17 additions & 4 deletions backend/core/config.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import os
from functools import lru_cache

from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic_settings import BaseSettings


class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=f'.env.{os.getenv("MODE","dev")}', extra='allow')
# Required settings with their types
minio_access_key: str
minio_secret_key: str
postgres_user: str
postgres_password: str
postgres_db: str
postgres_ip: str
postgres_port: str
google_redirect_uri: str
google_client_id: str
google_client_secret: str
google_allowed_domains: str
jwt_secret_key: str
jwt_algorithm: str
jwt_access_token_expire_minutes: str


@lru_cache
def get_settings():
return Settings(_env_file=f'.env.{os.getenv("MODE","dev")}')
return Settings()
10 changes: 4 additions & 6 deletions backend/core/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,25 @@ def create_auth_flow(self) -> Tuple[str, str]:
pass

@abstractmethod
def process_oauth_callback(
self,
db: Session,
code: str,
) -> Dict:
def process_oauth_callback(self, db: Session, code: str) -> Dict:
pass

@abstractmethod
def verify_email_domain(self, email: str) -> bool:
pass


class TokenService(Protocol):
def create_token(self, data: Dict) -> str:
pass

def verify_token(self, token: str) -> Dict:
pass


class CookieService(Protocol):
def set_auth_cookie(self, response: Response, user_id: str) -> None:
pass

def clear_auth_cookie(self, response: Response) -> None:
pass
pass
83 changes: 31 additions & 52 deletions backend/crud/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,123 +15,102 @@ class GoogleAuthProvider(OAuthProvider):
def __init__(self):
settings = get_settings()
self.client_config = {
"web": {
"client_id": settings.google_client_id,
"client_secret": settings.google_client_secret,
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"redirect_uris": [settings.google_redirect_uri]
'web': {
'client_id': settings.google_client_id,
'client_secret': settings.google_client_secret,
'auth_uri': 'https://accounts.google.com/o/oauth2/auth',
'token_uri': 'https://oauth2.googleapis.com/token',
'redirect_uris': [settings.google_redirect_uri],
}
}
self.scopes = [
'openid',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile'
'https://www.googleapis.com/auth/userinfo.profile',
]
self.user_crud = UserCRUD()

def create_auth_flow(self) -> Tuple[str, str]:
try:
settings = get_settings()
flow = Flow.from_client_config(
self.client_config,
scopes=self.scopes
)

flow = Flow.from_client_config(self.client_config, scopes=self.scopes)

flow.redirect_uri = settings.google_redirect_uri

authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true',
prompt='consent',
hd=settings.google_allowed_domains
hd=settings.google_allowed_domains,
)

return authorization_url, state

except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Failed to create authorization flow: {str(e)}"
status_code=500, detail=f'Failed to create authorization flow: {str(e)}'
)

def process_oauth_callback(
self,
db: Session,
code: str,
) -> Dict:
def process_oauth_callback(self, db: Session, code: str) -> Dict:
try:
settings = get_settings()
flow = Flow.from_client_config(
self.client_config,
scopes=self.scopes
)
flow = Flow.from_client_config(self.client_config, scopes=self.scopes)
flow.redirect_uri = settings.google_redirect_uri

flow.fetch_token(code=code)
credentials = flow.credentials

user_info = id_token.verify_oauth2_token(
credentials.id_token,
requests.Request(),
settings.google_client_id,
clock_skew_in_seconds=60
clock_skew_in_seconds=60,
)

if not self.verify_email_domain(user_info['email']):
raise HTTPException(
status_code=401,
detail='Only NCKU accounts are allowed to use this service.'
status_code=401, detail='Only NCKU accounts are allowed to use this service.'
)

google_user_info = {
'user_id': user_info['sub'],
'email': user_info['email'],
'username': user_info['name'],
'avatar': user_info.get('picture', '')
'avatar': user_info.get('picture', ''),
}

user_response = self.user_crud.get_or_create_user(
db=db,
google_user_info=google_user_info
db=db, google_user_info=google_user_info
)

return {
'user': user_response.data.model_dump(),
'access_token': credentials.token,
'refresh_token': credentials.refresh_token,
'token_expiry': credentials.expiry.timestamp() if credentials.expiry else None
'token_expiry': credentials.expiry.timestamp() if credentials.expiry else None,
}

except Exception:
raise HTTPException(
status_code=400,
detail='Failed to process OAuth callback'
)
raise HTTPException(status_code=400, detail='Failed to process OAuth callback')

def verify_email_domain(self, email: str) -> bool:
settings = get_settings()
return email.endswith(f'@{settings.google_allowed_domains}')

def _verify_oauth_token(self, code: str) -> Dict:
settings = get_settings()
flow = Flow.from_client_config(
self.client_config,
scopes=self.scopes
)
flow = Flow.from_client_config(self.client_config, scopes=self.scopes)
flow.redirect_uri = settings.google_redirect_uri
flow.fetch_token(code=code)

return id_token.verify_oauth2_token(
flow.credentials.id_token,
requests.Request(),
settings.google_client_id
flow.credentials.id_token, requests.Request(), settings.google_client_id
)

def _extract_user_info(self, id_info: Dict) -> Dict:
return {
'id': id_info['sub'],
'email': id_info['email'],
'name': id_info['name'],
'picture': id_info.get('picture')
'picture': id_info.get('picture'),
}
24 changes: 10 additions & 14 deletions backend/crud/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ async def create_file(
user = db.query(User).filter(User.user_id == file_data.uploader_id).first()
if not user:
raise HTTPException(
status_code=404,
detail=f'User with id ${file_data.uploader_id} not found.'
status_code=404, detail=f'User with id ${file_data.uploader_id} not found.'
)

file_id = str(uuid4())
Expand All @@ -57,27 +56,27 @@ async def create_file(
f.write(content)
except Exception:
raise HTTPException(status_code=500, detail='Failed to save file.')

try:
db_file = File(
filename=file_data.filename,
file_location=file_path,
uploader_id=file_data.uploader_id,
file_id=file_id
file_id=file_id,
)

db.add(db_file)
db.commit()
db.refresh(db_file)

return ResponseModel(
status=ResponseStatus.SUCCESS,
message="File uploaded successfully",
data=db_file
message='File uploaded successfully',
data=db_file,
)
except Exception:
raise HTTPException(status_code=500, detail="Failed to create file record")
raise HTTPException(status_code=500, detail='Failed to create file record')

except HTTPException:
if file_path and os.path.exists(file_path):
os.remove(file_path)
Expand All @@ -87,10 +86,7 @@ async def create_file(
if file_path and os.path.exists(file_path):
os.remove(file_path)

raise HTTPException(
status_code=500,
detail=f"Failed to process file upload: {str(e)}"
)
raise HTTPException(status_code=500, detail=f'Failed to process file upload: {str(e)}')

def read_all_file(self, db: Session) -> ResponseModel[List[FileResponseSchema]]:
try:
Expand Down
Loading

0 comments on commit 6a2a58e

Please sign in to comment.