diff --git a/alembic/versions/147ef859a38e_first_comment.py b/alembic/versions/147ef859a38e_first_comment.py new file mode 100644 index 0000000..d473291 --- /dev/null +++ b/alembic/versions/147ef859a38e_first_comment.py @@ -0,0 +1,24 @@ +"""first comment + +Revision ID: 147ef859a38e +Revises: +Create Date: 2022-05-29 11:20:41.685332 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '147ef859a38e' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + pass + + +def downgrade(): + pass diff --git a/api/user_api.py b/api/user_api.py index fb8b2e1..64a927c 100644 --- a/api/user_api.py +++ b/api/user_api.py @@ -5,10 +5,20 @@ # @File :user_api.py from fastapi import APIRouter, Body, Depends, Query +from typing_extensions import Annotated +from entity.user_entity import UserFrom +from sqlalchemy.orm import Session +from config.db_session import get_db +from service.user_service import user_service +from utils.response import success_200,error_211 router = APIRouter(prefix="/api", tags=["用户模块"]) -@router.post("/register/user") -def register(): - return {"code": 200, "message": "用户注册成功"} +@router.post("/register/user", summary="注册用户", name="注册用户") +def register(user: Annotated[UserFrom, Body(...)], db: Session = Depends(get_db)): + user = user_service(db, user) + if isinstance(user,dict): + return success_200(data=user, message="用户注册成功") + return error_211(message=user) + diff --git a/config/db_session.py b/config/db_session.py new file mode 100644 index 0000000..74c1982 --- /dev/null +++ b/config/db_session.py @@ -0,0 +1,37 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:26 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :db_session.py.py +from typing import Generator +from config.database import SessionLocal +from sqlalchemy.orm import Session + + +def get_db() -> Generator: + db = SessionLocal() + try: + yield db + finally: + db.close() + + +def db_add(db: Session, model: object): + db.add(model) + db.commit() + db.refresh(model) + + +def db_save(db: Session, model: object): + db.commit() + db.flush() + db.refresh(model) + + +def db_delete(db: Session, model: object): + db.delete(model) + db.commit() + db.flush() + + +__all__ = ["get_db", "db_add", "db_save","db_delete"] \ No newline at end of file diff --git a/config/factory.py b/config/factory.py new file mode 100644 index 0000000..ef4b971 --- /dev/null +++ b/config/factory.py @@ -0,0 +1,25 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:51 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :factory.py +from datetime import datetime + + +class AutomationResponse(object): + @staticmethod + def model_to_dict(obj, *ignore: str): + data = dict() + for c in obj.__table__.columns: + if c.name in ignore: + continue + val = getattr(obj, c.name) + if isinstance(val, datetime): + data[c.name] = val.strftime("%Y-%m-%d %H:%M:%S") + else: + data[c.name] = val + return data + + @staticmethod + def model_to_list(data: list, *ignore: str): + return [AutomationResponse.model_to_dict(x, *ignore) for x in data] \ No newline at end of file diff --git a/entity/user_entity.py b/entity/user_entity.py new file mode 100644 index 0000000..48714ec --- /dev/null +++ b/entity/user_entity.py @@ -0,0 +1,13 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:22 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :user_entity.py.py +from pydantic import BaseModel, EmailStr +from typing import Optional + + +class UserFrom(BaseModel): + username: str + password: str + email: EmailStr diff --git a/enums/enums.py b/enums/enums.py new file mode 100644 index 0000000..2b022da --- /dev/null +++ b/enums/enums.py @@ -0,0 +1,15 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:47 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :enums.py.py +from enum import Enum + + +class RegisterUser(Enum): + USER_IS_EXISTS_ALREADY = "用户名或邮箱已存在,请使用您的用户名登录" + USER_CONST = "用户可以进行注册" + USER_ID_ERROR = "用户不存在,请检查规范数据" + DELETE_USER_SUCCESS = "用户删除成功" + LOGIN_USER_ERROR = "用户名或密码错误" + USER_DISABLED = "用户已被禁用,请联系管理员" \ No newline at end of file diff --git a/logs/automation_info.log b/logs/automation_info.log index 942484c..2a88315 100644 --- a/logs/automation_info.log +++ b/logs/automation_info.log @@ -318,3 +318,53 @@ 2022-05-29 11:13:54 | MainThread:8595076608 | create_app:app:37 - INFO - Server address http://192.168.1.107:8000 2022-05-29 11:13:54 | MainThread:8595076608 | create_app:app:38 - INFO - Api doc address http://192.168.1.107:8000/docs 2022-05-29 11:13:54 | MainThread:8595076608 | create_app:app:39 - INFO - Api redoc address http://192.168.1.107:8000/redoc +2022-05-29 11:20:51 | MainThread:8642307584 | :__init__:16 - INFO - start the automation service development environment +2022-05-29 11:20:51 | MainThread:8642307584 | :__init__:22 - INFO - loading environment configuration file +2022-05-29 11:21:09 | MainThread:8671749632 | :__init__:16 - INFO - start the automation service development environment +2022-05-29 11:21:09 | MainThread:8671749632 | :__init__:22 - INFO - loading environment configuration file +2022-05-29 11:23:47 | MainThread:8639759872 | :__init__:16 - INFO - start the automation service development environment +2022-05-29 11:23:47 | MainThread:8639759872 | :__init__:22 - INFO - loading environment configuration file +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:15 - INFO - loading application configuration +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:16 - INFO - create FastApi app object +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:25 - INFO - Adding a New route success +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:26 - INFO - Start registering middleware +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:36 - INFO - Application started successfully:CORSMiddleware +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:37 - INFO - Server address http://192.168.1.107:8000 +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:38 - INFO - Api doc address http://192.168.1.107:8000/docs +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:39 - INFO - Api redoc address http://192.168.1.107:8000/redoc +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:15 - INFO - loading application configuration +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:16 - INFO - create FastApi app object +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:25 - INFO - Adding a New route success +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:26 - INFO - Start registering middleware +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:36 - INFO - Application started successfully:CORSMiddleware +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:37 - INFO - Server address http://192.168.1.107:8000 +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:38 - INFO - Api doc address http://192.168.1.107:8000/docs +2022-05-29 11:23:47 | MainThread:8639759872 | create_app:app:39 - INFO - Api redoc address http://192.168.1.107:8000/redoc +2022-05-29 11:26:08 | MainThread:8675997184 | :__init__:16 - INFO - start the automation service development environment +2022-05-29 11:26:08 | MainThread:8675997184 | :__init__:22 - INFO - loading environment configuration file +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:15 - INFO - loading application configuration +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:16 - INFO - create FastApi app object +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:25 - INFO - Adding a New route success +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:26 - INFO - Start registering middleware +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:36 - INFO - Application started successfully:CORSMiddleware +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:37 - INFO - Server address http://192.168.1.107:8000 +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:38 - INFO - Api doc address http://192.168.1.107:8000/docs +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:39 - INFO - Api redoc address http://192.168.1.107:8000/redoc +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:15 - INFO - loading application configuration +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:16 - INFO - create FastApi app object +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:25 - INFO - Adding a New route success +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:26 - INFO - Start registering middleware +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:36 - INFO - Application started successfully:CORSMiddleware +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:37 - INFO - Server address http://192.168.1.107:8000 +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:38 - INFO - Api doc address http://192.168.1.107:8000/docs +2022-05-29 11:26:08 | MainThread:8675997184 | create_app:app:39 - INFO - Api redoc address http://192.168.1.107:8000/redoc +2022-05-29 11:41:47 | MainThread:8607176192 | :__init__:16 - INFO - start the automation service development environment +2022-05-29 11:41:47 | MainThread:8607176192 | :__init__:22 - INFO - loading environment configuration file +2022-05-29 11:47:43 | MainThread:8594392576 | :__init__:16 - INFO - start the automation service development environment +2022-05-29 11:47:43 | MainThread:8594392576 | :__init__:22 - INFO - loading environment configuration file +2022-05-29 11:48:30 | MainThread:8627926528 | :__init__:16 - INFO - start the automation service development environment +2022-05-29 11:48:30 | MainThread:8627926528 | :__init__:22 - INFO - loading environment configuration file +2022-05-29 11:51:39 | MainThread:8638133760 | :__init__:16 - INFO - start the automation service development environment +2022-05-29 11:51:39 | MainThread:8638133760 | :__init__:22 - INFO - loading environment configuration file +2022-05-29 11:56:17 | MainThread:8672949760 | :__init__:16 - INFO - start the automation service development environment +2022-05-29 11:56:17 | MainThread:8672949760 | :__init__:22 - INFO - loading environment configuration file diff --git a/repository/__init__.py b/repository/__init__.py new file mode 100644 index 0000000..ce27a3a --- /dev/null +++ b/repository/__init__.py @@ -0,0 +1,5 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:28 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :__init__.py.py diff --git a/repository/user_repository.py b/repository/user_repository.py new file mode 100644 index 0000000..a7d3704 --- /dev/null +++ b/repository/user_repository.py @@ -0,0 +1,25 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:29 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :user_repository.py +from sqlalchemy import or_ +from sqlalchemy.orm import Session + +from config.db_session import db_add +from config.factory import AutomationResponse +from entity.user_entity import UserFrom +from models.model import User + + +def check_user_email(db: Session, user: UserFrom) -> bool: + user = db.query(User).filter(or_(User.username == user.username, user.email == user.email)).first() + if user: + return True + return False + + +def register_user(db: Session, user: UserFrom)->dict: + us_er = User(**user.dict()) + db_add(db, us_er) + return AutomationResponse.model_to_dict(us_er, "password") diff --git a/service/__init__.py b/service/__init__.py new file mode 100644 index 0000000..c31354d --- /dev/null +++ b/service/__init__.py @@ -0,0 +1,5 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:44 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :__init__.py.py diff --git a/service/user_service.py b/service/user_service.py new file mode 100644 index 0000000..625b724 --- /dev/null +++ b/service/user_service.py @@ -0,0 +1,19 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:45 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :user_service.py +from sqlalchemy.orm import Session +from entity.user_entity import UserFrom +from enums.enums import RegisterUser +from repository.user_repository import check_user_email +from utils.jwt_token import get_md5_pwd +from repository.user_repository import register_user + + +def user_service(db: Session, user: UserFrom): + user.password = get_md5_pwd(user.password) + is_true = check_user_email(db, user) + if is_true: + return RegisterUser.USER_IS_EXISTS_ALREADY + return register_user(db, user) diff --git a/utils/jsonable_encoder.py b/utils/jsonable_encoder.py new file mode 100644 index 0000000..b50fee3 --- /dev/null +++ b/utils/jsonable_encoder.py @@ -0,0 +1,158 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:40 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :jsonable_encoder.py.py +import dataclasses +from collections import defaultdict +from enum import Enum +from pathlib import PurePath +from types import GeneratorType +from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union + +from pydantic import BaseModel +from pydantic.json import ENCODERS_BY_TYPE + +SetIntStr = Set[Union[int, str]] +DictIntStrAny = Dict[Union[int, str], Any] + + +def generate_encoders_by_class_tuples( + type_encoder_map: Dict[Any, Callable[[Any], Any]] +) -> Dict[Callable[[Any], Any], Tuple[Any, ...]]: + encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict( + tuple + ) + for type_, encoder in type_encoder_map.items(): + encoders_by_class_tuples[encoder] += (type_,) + return encoders_by_class_tuples + + +encoders_by_class_tuples = generate_encoders_by_class_tuples(ENCODERS_BY_TYPE) + + +def jsonable_encoder( + obj: Any, + include: Optional[Union[SetIntStr, DictIntStrAny]] = None, + exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + custom_encoder: Dict[Any, Callable[[Any], Any]] = {}, + sqlalchemy_safe: bool = True, +) -> Any: + if include is not None and not isinstance(include, (set, dict)): + include = set(include) + if exclude is not None and not isinstance(exclude, (set, dict)): + exclude = set(exclude) + if isinstance(obj, BaseModel): + encoder = getattr(obj.__config__, "json_encoders", {}) + if custom_encoder: + encoder.update(custom_encoder) + obj_dict = obj.dict( + include=include, # type: ignore # in Pydantic + exclude=exclude, # type: ignore # in Pydantic + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_none=exclude_none, + exclude_defaults=exclude_defaults, + ) + if "__root__" in obj_dict: + obj_dict = obj_dict["__root__"] + return jsonable_encoder( + obj_dict, + exclude_none=exclude_none, + exclude_defaults=exclude_defaults, + custom_encoder=encoder, + sqlalchemy_safe=sqlalchemy_safe, + ) + if dataclasses.is_dataclass(obj): + return dataclasses.asdict(obj) + if isinstance(obj, Enum): + return obj.value + if isinstance(obj, PurePath): + return str(obj) + if isinstance(obj, (str, int, float, type(None))): + return obj + if isinstance(obj, dict): + encoded_dict = {} + for key, value in obj.items(): + if ( + ( + not sqlalchemy_safe + or (not isinstance(key, str)) + or (not key.startswith("_sa")) + ) + and (value is not None or not exclude_none) + and ((include and key in include) or not exclude or key not in exclude) + ): + encoded_key = jsonable_encoder( + key, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_none=exclude_none, + custom_encoder=custom_encoder, + sqlalchemy_safe=sqlalchemy_safe, + ) + encoded_value = jsonable_encoder( + value, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_none=exclude_none, + custom_encoder=custom_encoder, + sqlalchemy_safe=sqlalchemy_safe, + ) + encoded_dict[encoded_key] = encoded_value + return encoded_dict + if isinstance(obj, (list, set, frozenset, GeneratorType, tuple)): + encoded_list = [] + for item in obj: + encoded_list.append( + jsonable_encoder( + item, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + custom_encoder=custom_encoder, + sqlalchemy_safe=sqlalchemy_safe, + ) + ) + return encoded_list + + if custom_encoder: + if type(obj) in custom_encoder: + return custom_encoder[type(obj)](obj) + else: + for encoder_type, encoder in custom_encoder.items(): + if isinstance(obj, encoder_type): + return encoder(obj) + + if type(obj) in ENCODERS_BY_TYPE: + return ENCODERS_BY_TYPE[type(obj)](obj) + for encoder, classes_tuple in encoders_by_class_tuples.items(): + if isinstance(obj, classes_tuple): + return encoder(obj) + + errors: List[Exception] = [] + try: + data = dict(obj) + except Exception as e: + errors.append(e) + try: + data = vars(obj) + except Exception as e: + errors.append(e) + raise ValueError(errors) + return jsonable_encoder( + data, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + custom_encoder=custom_encoder, + sqlalchemy_safe=sqlalchemy_safe, + ) \ No newline at end of file diff --git a/utils/jwt_token.py b/utils/jwt_token.py new file mode 100644 index 0000000..bd30e79 --- /dev/null +++ b/utils/jwt_token.py @@ -0,0 +1,55 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:41 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :jwt_token.py.py +import hashlib +from datetime import datetime, timedelta +from fastapi.security import OAuth2PasswordBearer +from fastapi import Depends, HTTPException, status +from passlib.context import CryptContext +from jose import jwt, JWTError +from config import settings + +crypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + + +def get_hash_pwd(password: str): + return crypt_context.hash(password) + + +def get_md5_pwd(pwd: str): + m = hashlib.md5() + m.update(pwd.encode("utf-8")) + return m.hexdigest() + + +def create_token(data: dict, expire_time): + if expire_time: + expire = datetime.utcnow() + expire_time + else: + expire = datetime.utcnow() + timedelta(minutes=30) + + data.update({"exp": expire}) + token = jwt.encode(data, settings.ACCESS.SECRET_KEY, algorithm=settings.ACCESS.ALGORITHM) + return token + + +oauth_scame = OAuth2PasswordBearer(tokenUrl="/api/login") + + +def parse_token(token: str = Depends(oauth_scame)): + token_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="token不正确或已过期", + headers={"WWW-Authenticate": "Bearer"} + ) + + try: + jwt_data = jwt.decode(token, settings.ACCESS.SECRET_KEY, algorithms=settings.ACCESS.ALGORITHM) + id = jwt_data.get("id") + if id is None or id == "": + raise token_exception + except JWTError: + raise token_exception + return id \ No newline at end of file diff --git a/utils/response.py b/utils/response.py new file mode 100644 index 0000000..507186e --- /dev/null +++ b/utils/response.py @@ -0,0 +1,44 @@ +# _*_ coding: utf-8 _*_ +# @Time :2022/5/29 11:39 +# @Email :508737091@qq.com +# @Author :qiangyanwen +# @File :response.py +from dataclasses import make_dataclass +from typing import Union +from fastapi.responses import JSONResponse +from fastapi import status + +from .jsonable_encoder import jsonable_encoder + + +def success_200(data: Union[list, dict, str] = None, code: int = 200, message: str = "Success") -> JSONResponse: + return response_base(status.HTTP_200_OK, data, code, message).Success() + + +def error_211(data: Union[list, dict, str] = None, code: int = 211, message: str = "error") -> JSONResponse: + return response_base(status.HTTP_200_OK, data, code, message).Success() + + +def error_400(data: Union[list, dict, str] = None, code: int = 400, message: str = "请求失败") -> JSONResponse: + return response_base(status.HTTP_400_BAD_REQUEST, data, code, message).Success() + + +def error_403(data: Union[list, dict, str] = None, code: int = 403, message: str = "Token 认证失败") -> JSONResponse: + return response_base(status.HTTP_403_FORBIDDEN, data, code, message).Success() + + +def error_500(data: Union[list, dict, str] = None, code: int = 500, + message: str = "Internal Server Error") -> JSONResponse: + return response_base(status.HTTP_500_INTERNAL_SERVER_ERROR, data, code, message).Success() + + +response_base = make_dataclass( + "response_base", + [('http_status', int, None), ('data', Union[list, dict, str], None), ('code', int, None), ('message', str, None)], + namespace={ + "Success": lambda self: JSONResponse( + status_code=self.http_status, + content=jsonable_encoder({'code': self.code, 'message': self.message, 'data': self.data}) + ) + } +) \ No newline at end of file