-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7c7da9a
commit 803e534
Showing
30 changed files
with
868 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
# Client | ||
node_modules | ||
dist | ||
.env | ||
.env.production | ||
|
||
# Server | ||
__pycache__ | ||
.venv | ||
.DS_STORE | ||
poetry.lock | ||
*.log | ||
.ignore | ||
|
||
# temp | ||
cloudbuild.yaml | ||
docker-compose.yaml | ||
Makefile | ||
start.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
{ | ||
// The following will hide the js and map files in the editor | ||
"files.exclude": { | ||
"**/__pycache__": true | ||
}, | ||
"files.trimTrailingWhitespace": true, | ||
"editor.codeActionsOnSave": { | ||
"source.organizeImports": "explicit", | ||
"source.fixAll": "explicit" | ||
}, | ||
"cSpell.words": ["tanstack"] | ||
} | ||
} |
File renamed without changes.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
LOG_LEVEL=WARN | ||
ENV=development | ||
|
||
# MongoDB Config | ||
MONGO_DB_NAME=db | ||
ATLAS_USERNAME=admin | ||
ATLAS_PASSWORD=password | ||
MAX_CONNECTIONS_COUNT=100 | ||
MIN_CONNECTIONS_COUNT=0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
FROM python:3.12 | ||
|
||
WORKDIR /code | ||
|
||
COPY ./requirements.txt /code/requirements.txt | ||
|
||
RUN pip install --upgrade pip | ||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt | ||
|
||
COPY . /code | ||
|
||
EXPOSE 80 | ||
|
||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# uservote-api | ||
|
||
## Description | ||
|
||
FastAPI based API for uservote project. | ||
|
||
## Requirements | ||
|
||
Python 3.8+ | ||
|
||
## Installation | ||
|
||
```bash | ||
pip install -r requirements.txt | ||
``` | ||
|
||
## Usage | ||
|
||
```bash | ||
bash start.sh | ||
``` | ||
|
||
## License | ||
|
||
MIT |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from fastapi import APIRouter, Depends | ||
from fastapi.responses import JSONResponse | ||
from motor.core import AgnosticDatabase | ||
import platform | ||
import psutil | ||
|
||
from app.database.database import get_database_client | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get('/', include_in_schema=False) | ||
@router.get('') | ||
async def health(db: AgnosticDatabase = Depends(get_database_client)): | ||
try: | ||
# Check if the database is responsive | ||
await db.command('ping') | ||
db_status = 'up' | ||
except Exception: | ||
db_status = 'down' | ||
|
||
# Get system information | ||
system_info = { | ||
"system": platform.system(), | ||
"processor": platform.processor(), | ||
"architecture": platform.architecture(), | ||
"memory": psutil.virtual_memory()._asdict(), | ||
"disk": psutil.disk_usage('/')._asdict() | ||
} | ||
|
||
return JSONResponse( | ||
status_code=db_status == 'up' and 200 or 503, | ||
content={ | ||
"database": db_status, | ||
"system_info": system_info | ||
} | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
from fastapi import APIRouter, Depends, Response | ||
from uuid import UUID | ||
|
||
import logging | ||
from app.database.database import get_database_client, AgnosticDatabase | ||
from app.common.utils import uuid_masker | ||
from app.common.error import UnprocessableError | ||
from app.schema.feature_request import \ | ||
get_feature_requests as db_get_feature_requests, \ | ||
get_feature_request_by_id as db_get_feature_request_by_id, \ | ||
create_feature_request as db_create_feature_request, \ | ||
update_feature_request as db_update_feature_request, \ | ||
delete_feature_request as db_delete_feature_request | ||
from app.models.feature_request import FeatureRequestCreateRequest, FeatureRequestGetResponse, FeatureRequestCreateResponse | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get('/', include_in_schema=False, status_code=200) | ||
@router.get('', response_model=list, status_code=200, responses={400: {}}) | ||
async def get_feature_requests( | ||
db: AgnosticDatabase = Depends(get_database_client), | ||
): | ||
logging.info('Received get feature requests request') | ||
|
||
feature_requests = await db_get_feature_requests(db) | ||
|
||
if None is feature_requests: | ||
return Response(status_code=404) | ||
|
||
return feature_requests | ||
|
||
|
||
@router.get('/{id}', include_in_schema=False, status_code=200) | ||
@router.get('/{id}', response_model=FeatureRequestGetResponse, status_code=200, responses={400: {}}) | ||
async def get_feature_request_by_id( | ||
id: UUID, | ||
db: AgnosticDatabase = Depends(get_database_client), | ||
): | ||
logging.info( | ||
f'Received get feature request {uuid_masker(id)} request' | ||
) | ||
|
||
feature_request = await db_get_feature_request_by_id( | ||
db, | ||
id | ||
) | ||
|
||
if None is feature_request: | ||
return Response(status_code=404) | ||
|
||
return FeatureRequestGetResponse( | ||
id=feature_request.get("_id"), | ||
created_at=feature_request.get("created_at"), | ||
updated_at=feature_request.get("updated_at"), | ||
title=feature_request.get("title"), | ||
content=feature_request.get("content"), | ||
votes=feature_request.get("votes"), | ||
category=feature_request.get("category"), | ||
author_username=feature_request.get("author_username"), | ||
comments=feature_request.get("comments"), | ||
) | ||
|
||
|
||
@router.post('/', include_in_schema=False, status_code=201) | ||
@router.post('', response_model=FeatureRequestCreateResponse, status_code=201, responses={400: {}}) | ||
async def create_feature_request( | ||
feature_request_data: FeatureRequestCreateRequest, | ||
db: AgnosticDatabase = Depends(get_database_client) | ||
): | ||
logging.info('Received create feature request request') | ||
|
||
# TODO: return actual document, not the user's input | ||
feature_request_db = await db_create_feature_request( | ||
db, | ||
feature_request_data.title, | ||
feature_request_data.content, | ||
feature_request_data.author_username | ||
) | ||
|
||
return FeatureRequestCreateResponse(id=feature_request_db.id) | ||
|
||
|
||
@router.put('/{id}', include_in_schema=False, status_code=200) | ||
@router.put('/{id}', status_code=200, responses={400: {}}) | ||
async def update_feature_request( | ||
id: UUID, | ||
feature_request_data: FeatureRequestCreateRequest, | ||
db: AgnosticDatabase = Depends(get_database_client), | ||
): | ||
logging.info( | ||
f'Received update feature request {uuid_masker(id)} request' | ||
) | ||
|
||
feature_request_db = await db_update_feature_request( | ||
db, | ||
id, | ||
feature_request_data.model_dump() | ||
) | ||
if None is feature_request_db: | ||
raise UnprocessableError([]) | ||
|
||
return FeatureRequestCreateResponse(id=feature_request_db.get("_id")) | ||
|
||
|
||
@router.delete('/{id}', include_in_schema=False, status_code=200) | ||
@router.delete('/{id}', status_code=200, responses={400: {}}) | ||
async def delete_feature_request( | ||
id: UUID, | ||
db: AgnosticDatabase = Depends(get_database_client), | ||
): | ||
logging.info( | ||
f'Received delete feature request {uuid_masker(id)} request' | ||
) | ||
|
||
feature_request_db = await db_delete_feature_request( | ||
db, | ||
id, | ||
) | ||
if None is feature_request_db: | ||
raise UnprocessableError([]) | ||
return Response(status_code=204) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from fastapi.responses import JSONResponse | ||
|
||
|
||
class BaseErrResp(Exception): | ||
def __init__(self, status: int, title: str, details: list) -> None: | ||
self.__status = status | ||
self.__title = title | ||
self.__detail = details | ||
|
||
def gen_err_resp(self) -> JSONResponse: | ||
return JSONResponse( | ||
status_code=self.__status, | ||
content={ | ||
"type": "about:blank", | ||
'title': self.__title, | ||
'status': self.__status, | ||
'detail': self.__detail | ||
} | ||
) | ||
|
||
|
||
class BadRequest(BaseErrResp): | ||
def __init__(self, details: list): | ||
super(BadRequest, self).__init__(400, 'Bad Request', details) | ||
|
||
|
||
class InternalError(BaseErrResp): | ||
def __init__(self, details: list): | ||
super(InternalError, self).__init__(500, 'Internal Error', details) | ||
|
||
|
||
class UnprocessableError(BaseErrResp): | ||
def __init__(self, details: list): | ||
super(UnprocessableError, self).__init__( | ||
422, | ||
'Unprocessable Entity', | ||
details | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import re | ||
from uuid import UUID | ||
|
||
|
||
def to_lower_camel_case(string: str) -> str: | ||
split_str = string.split('_') | ||
return split_str[0] + ''.join(word.capitalize() for word in split_str[1:]) | ||
|
||
|
||
def uuid_masker(exposed_uuid: str | UUID) -> str: | ||
uuid_str = str(exposed_uuid) | ||
return re.sub( | ||
r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-", | ||
'********-****-****-****-', | ||
uuid_str | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import os | ||
from dotenv import load_dotenv | ||
import logging | ||
|
||
from app.common.error import InternalError | ||
|
||
load_dotenv() | ||
|
||
|
||
class Config: | ||
version = "0.1.0" | ||
title = "UserVote API" | ||
env = os.getenv('ENV') | ||
|
||
app_settings = { | ||
'mongo_db_name': os.getenv('DB_NAME'), | ||
'mongo_username': os.getenv('ATLAS_USERNAME'), | ||
'mongo_password': os.getenv('ATLAS_PASSWORD'), | ||
'max_db_conn_count': os.getenv('MAX_CONNECTIONS_COUNT'), | ||
'min_db_conn_count': os.getenv('MIN_CONNECTIONS_COUNT'), | ||
} | ||
|
||
@classmethod | ||
def validate_app_settings(cls): | ||
for k, v in cls.app_settings.items(): | ||
if v is None: | ||
logging.error(f'Config variable error. {k} cannot be None') | ||
raise InternalError([{"message": "Server configure error"}]) | ||
else: | ||
logging.info(f'Config variable {k} is {v}') |
Oops, something went wrong.