initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.idea/
|
||||
__pycache__
|
||||
16
back/Dockerfile
Normal file
16
back/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM python:3.10
|
||||
|
||||
# make the 'app' folder the current working directory
|
||||
WORKDIR /code
|
||||
|
||||
# copy both 'package.json' and 'package-lock.json' (if available)
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
# install project dependencies
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# copy project files and folders to the current working directory (i.e. 'app' folder)
|
||||
COPY ./app /code/app
|
||||
|
||||
EXPOSE 8000
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
0
back/app/__init__.py
Normal file
0
back/app/__init__.py
Normal file
1
back/app/contract/__init__.py
Normal file
1
back/app/contract/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .routes import router as contract_router
|
||||
BIN
back/app/contract/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
back/app/contract/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/contract/__pycache__/models.cpython-310.pyc
Normal file
BIN
back/app/contract/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/contract/__pycache__/routes.cpython-310.pyc
Normal file
BIN
back/app/contract/__pycache__/routes.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/contract/__pycache__/schemas.cpython-310.pyc
Normal file
BIN
back/app/contract/__pycache__/schemas.cpython-310.pyc
Normal file
Binary file not shown.
40
back/app/contract/models.py
Normal file
40
back/app/contract/models.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from beanie import Document
|
||||
|
||||
from ..entity.models import Entity
|
||||
|
||||
|
||||
class ContractType(str, Enum):
|
||||
employment = 'employment'
|
||||
location = 'location'
|
||||
|
||||
|
||||
class ContractStatus(str, Enum):
|
||||
new = 'new'
|
||||
signed = 'signed'
|
||||
in_effect = 'in_effect'
|
||||
executed = 'executed'
|
||||
|
||||
|
||||
class Party(BaseModel):
|
||||
entity: Entity
|
||||
part: str
|
||||
|
||||
|
||||
class Clause(BaseModel):
|
||||
name: str
|
||||
body: str
|
||||
|
||||
|
||||
class Contract(Document):
|
||||
_id: str
|
||||
type: ContractType
|
||||
parties: List[Party]
|
||||
clauses: List[Clause]
|
||||
status: ContractStatus = Field(default=ContractStatus.new)
|
||||
created_at: datetime = Field(default=datetime.utcnow(), nullable=False)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)
|
||||
5
back/app/contract/routes.py
Normal file
5
back/app/contract/routes.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from ..core.routes import get_crud_router
|
||||
from .models import Contract
|
||||
from .schemas import ContractCreate, ContractRead, ContractUpdate
|
||||
|
||||
router = get_crud_router(Contract, ContractCreate, ContractRead, ContractUpdate)
|
||||
36
back/app/contract/schemas.py
Normal file
36
back/app/contract/schemas.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel, validator
|
||||
|
||||
from beanie import PydanticObjectId
|
||||
|
||||
from .models import Contract, ContractType, Clause
|
||||
from ..entity.models import Entity
|
||||
from ..core.schemas import Writer
|
||||
|
||||
|
||||
class ContractRead(Contract):
|
||||
pass
|
||||
|
||||
|
||||
class PartyCreate(BaseModel):
|
||||
entity: PydanticObjectId
|
||||
part: str
|
||||
|
||||
|
||||
class ContractCreate(Writer):
|
||||
type: ContractType
|
||||
parties: List[PartyCreate]
|
||||
clauses: List[Clause]
|
||||
|
||||
async def validate_foreign_key(self):
|
||||
for p in self.parties:
|
||||
p.entity = await Entity.get(p.entity)
|
||||
if p.entity is None:
|
||||
raise ValueError
|
||||
|
||||
|
||||
class ContractUpdate(BaseModel):
|
||||
status: str
|
||||
0
back/app/core/__init__.py
Normal file
0
back/app/core/__init__.py
Normal file
BIN
back/app/core/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
back/app/core/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/core/__pycache__/routes.cpython-310.pyc
Normal file
BIN
back/app/core/__pycache__/routes.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/core/__pycache__/schemas.cpython-310.pyc
Normal file
BIN
back/app/core/__pycache__/schemas.cpython-310.pyc
Normal file
Binary file not shown.
64
back/app/core/routes.py
Normal file
64
back/app/core/routes.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from beanie import PydanticObjectId
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from typing import TypeVar, List, Generic
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
U = TypeVar('U')
|
||||
V = TypeVar('V')
|
||||
W = TypeVar('W')
|
||||
|
||||
|
||||
def get_crud_router(model, model_create, model_read, model_update):
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/", response_description="{} added to the database".format(model.__name__))
|
||||
async def create(item: model_create) -> dict:
|
||||
await item.validate_foreign_key()
|
||||
o = await model(**item.dict()).create()
|
||||
return {"message": "{} added successfully".format(model.__name__), "id": o}
|
||||
|
||||
@router.get("/{id}", response_description="{} record retrieved".format(model.__name__))
|
||||
async def read_id(id: PydanticObjectId) -> model_read:
|
||||
item = await model.get(id)
|
||||
return item
|
||||
|
||||
@router.get("/", response_description="{} records retrieved".format(model.__name__))
|
||||
async def read_list() -> List[model_read]:
|
||||
item = await model.find_all().to_list()
|
||||
return item
|
||||
|
||||
@router.put("/{id}", response_description="{} record updated".format(model.__name__))
|
||||
async def update(id: PydanticObjectId, req: model_update) -> model_read:
|
||||
req = {k: v for k, v in req.dict().items() if v is not None}
|
||||
update_query = {"$set": {
|
||||
field: value for field, value in req.items()
|
||||
}}
|
||||
|
||||
item = await model.get(id)
|
||||
if not item:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="{} record not found!".format(model.__name__)
|
||||
)
|
||||
|
||||
await item.update(update_query)
|
||||
return item
|
||||
|
||||
@router.delete("/{id}", response_description="{} record deleted from the database".format(model.__name__))
|
||||
async def delete(id: PydanticObjectId) -> dict:
|
||||
item = await model.get(id)
|
||||
|
||||
if not item:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="{} record not found!".format(model.__name__)
|
||||
)
|
||||
|
||||
await item.delete()
|
||||
return {
|
||||
"message": "{} deleted successfully".format(model.__name__)
|
||||
}
|
||||
|
||||
return router
|
||||
12
back/app/core/schemas.py
Normal file
12
back/app/core/schemas.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Reader(BaseModel):
|
||||
pass
|
||||
# class Config:
|
||||
# fields = {'id': '_id'}
|
||||
|
||||
|
||||
class Writer(BaseModel):
|
||||
async def validate_foreign_key(self):
|
||||
pass
|
||||
18
back/app/db.py
Normal file
18
back/app/db.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import motor.motor_asyncio
|
||||
|
||||
from beanie import init_beanie
|
||||
|
||||
from .user import User, AccessToken
|
||||
from .entity.models import Entity
|
||||
from .order.models import Order
|
||||
from .contract.models import Contract
|
||||
|
||||
DATABASE_URL = "mongodb://root:example@mongo:27017/"
|
||||
|
||||
|
||||
async def init_db():
|
||||
client = motor.motor_asyncio.AsyncIOMotorClient(
|
||||
DATABASE_URL, uuidRepresentation="standard"
|
||||
)
|
||||
|
||||
await init_beanie(database=client.db_name, document_models=[User, AccessToken, Entity, Order, Contract, ], )
|
||||
1
back/app/entity/__init__.py
Normal file
1
back/app/entity/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .routes import router as entity_router
|
||||
BIN
back/app/entity/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
back/app/entity/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/entity/__pycache__/models.cpython-310.pyc
Normal file
BIN
back/app/entity/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/entity/__pycache__/routes.cpython-310.pyc
Normal file
BIN
back/app/entity/__pycache__/routes.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/entity/__pycache__/schemas.cpython-310.pyc
Normal file
BIN
back/app/entity/__pycache__/schemas.cpython-310.pyc
Normal file
Binary file not shown.
20
back/app/entity/models.py
Normal file
20
back/app/entity/models.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from enum import Enum
|
||||
from datetime import datetime
|
||||
from pydantic import Field
|
||||
|
||||
from beanie import Document
|
||||
|
||||
|
||||
class EntityType(str, Enum):
|
||||
individual = 'individual'
|
||||
corporation = 'corporation'
|
||||
institution = 'institution'
|
||||
|
||||
|
||||
class Entity(Document):
|
||||
_id: str
|
||||
type: EntityType
|
||||
name: str
|
||||
address: str
|
||||
created_at: datetime = Field(default=datetime.utcnow(), nullable=False)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)
|
||||
7
back/app/entity/routes.py
Normal file
7
back/app/entity/routes.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from ..core.routes import get_crud_router
|
||||
from .models import Entity
|
||||
from .schemas import EntityCreate, EntityRead, EntityUpdate
|
||||
|
||||
router = get_crud_router(Entity, EntityCreate, EntityRead, EntityUpdate)
|
||||
|
||||
|
||||
20
back/app/entity/schemas.py
Normal file
20
back/app/entity/schemas.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import uuid
|
||||
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .models import Entity, EntityType
|
||||
|
||||
|
||||
class EntityRead(Entity):
|
||||
pass
|
||||
|
||||
|
||||
class EntityCreate(BaseModel):
|
||||
type: EntityType
|
||||
name: str
|
||||
address: str
|
||||
|
||||
|
||||
class EntityUpdate(BaseModel):
|
||||
name: str
|
||||
33
back/app/main.py
Normal file
33
back/app/main.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi import Depends, Request
|
||||
|
||||
from .contract import contract_router
|
||||
from .db import init_db
|
||||
from .user import user_router, get_auth_router
|
||||
from .entity import entity_router
|
||||
from .order import order_router
|
||||
from .template import template_router
|
||||
|
||||
app = FastAPI(root_path="/api/v1")
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def on_startup():
|
||||
await init_db()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "Hello World"}
|
||||
|
||||
|
||||
app.include_router(get_auth_router(), prefix="/auth", tags=["auth"], )
|
||||
app.include_router(user_router, prefix="/users", tags=["users"], )
|
||||
app.include_router(entity_router, prefix="/entity", tags=["entity"], )
|
||||
app.include_router(order_router, prefix="/order", tags=["order"], )
|
||||
app.include_router(template_router, prefix="/template", tags=["template"], )
|
||||
app.include_router(contract_router, prefix="/contract", tags=["contract"], )
|
||||
|
||||
if __name__ == '__main__':
|
||||
import uvicorn
|
||||
uvicorn.run("main:app", host='0.0.0.0', port=8000, reload=True)
|
||||
1
back/app/order/__init__.py
Normal file
1
back/app/order/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .routes import router as order_router
|
||||
BIN
back/app/order/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
back/app/order/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/order/__pycache__/models.cpython-310.pyc
Normal file
BIN
back/app/order/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/order/__pycache__/routes.cpython-310.pyc
Normal file
BIN
back/app/order/__pycache__/routes.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/order/__pycache__/schemas.cpython-310.pyc
Normal file
BIN
back/app/order/__pycache__/schemas.cpython-310.pyc
Normal file
Binary file not shown.
13
back/app/order/models.py
Normal file
13
back/app/order/models.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from datetime import datetime
|
||||
|
||||
from beanie import Document
|
||||
|
||||
|
||||
class Order(Document):
|
||||
id: str
|
||||
client: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Settings:
|
||||
name = "order_collection"
|
||||
5
back/app/order/routes.py
Normal file
5
back/app/order/routes.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from ..core.routes import get_crud_router
|
||||
from .models import Order
|
||||
from .schemas import OrderCreate, OrderRead, OrderUpdate
|
||||
|
||||
router = get_crud_router(Order, OrderCreate, OrderRead, OrderUpdate)
|
||||
14
back/app/order/schemas.py
Normal file
14
back/app/order/schemas.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import uuid
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class OrderRead(BaseModel):
|
||||
pass
|
||||
|
||||
|
||||
class OrderCreate(BaseModel):
|
||||
login: str
|
||||
|
||||
|
||||
class OrderUpdate(BaseModel):
|
||||
pass
|
||||
9
back/app/template/__init__.py
Normal file
9
back/app/template/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .routes_contract import router as contract_router
|
||||
from .routes_clause import router as clause_router
|
||||
|
||||
template_router = APIRouter()
|
||||
|
||||
template_router.include_router(contract_router, prefix="/contract", tags=["template"], )
|
||||
template_router.include_router(clause_router, prefix="/clause", tags=["template"], )
|
||||
BIN
back/app/template/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
back/app/template/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/template/__pycache__/models.cpython-310.pyc
Normal file
BIN
back/app/template/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/template/__pycache__/routes_clause.cpython-310.pyc
Normal file
BIN
back/app/template/__pycache__/routes_clause.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/template/__pycache__/routes_contract.cpython-310.pyc
Normal file
BIN
back/app/template/__pycache__/routes_contract.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/template/__pycache__/schemas.cpython-310.pyc
Normal file
BIN
back/app/template/__pycache__/schemas.cpython-310.pyc
Normal file
Binary file not shown.
34
back/app/template/models.py
Normal file
34
back/app/template/models.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from beanie import Document
|
||||
|
||||
|
||||
class ContractType(str, Enum):
|
||||
individual = 'individual'
|
||||
corporation = 'corporation'
|
||||
|
||||
|
||||
class PartyTemplate(BaseModel):
|
||||
entity_id: str
|
||||
part: str
|
||||
name: str
|
||||
address: str
|
||||
|
||||
|
||||
class ClauseTemplate(Document):
|
||||
name: str
|
||||
body: str
|
||||
created_at: datetime = Field(default=datetime.utcnow(), nullable=False)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)
|
||||
|
||||
|
||||
class ContractTemplate(Document):
|
||||
id: str
|
||||
type: ContractType
|
||||
parties: List[PartyTemplate]
|
||||
clauses: List[ClauseTemplate]
|
||||
created_at: datetime = Field(default=datetime.utcnow(), nullable=False)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)
|
||||
5
back/app/template/routes_clause.py
Normal file
5
back/app/template/routes_clause.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from ..core.routes import get_crud_router
|
||||
from .models import ClauseTemplate
|
||||
from .schemas import ClauseTemplateCreate, ClauseTemplateRead, ClauseTemplateUpdate
|
||||
|
||||
router = get_crud_router(ClauseTemplate, ClauseTemplateCreate, ClauseTemplateRead, ClauseTemplateUpdate)
|
||||
5
back/app/template/routes_contract.py
Normal file
5
back/app/template/routes_contract.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from ..core.routes import get_crud_router
|
||||
from .models import ContractTemplate
|
||||
from .schemas import ContractTemplateCreate, ContractTemplateRead, ContractTemplateUpdate
|
||||
|
||||
router = get_crud_router(ContractTemplate, ContractTemplateCreate, ContractTemplateRead, ContractTemplateUpdate)
|
||||
32
back/app/template/schemas.py
Normal file
32
back/app/template/schemas.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import uuid
|
||||
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .models import ContractTemplate, ClauseTemplate
|
||||
|
||||
|
||||
class ContractTemplateRead(ContractTemplate):
|
||||
pass
|
||||
|
||||
|
||||
class ContractTemplateCreate(BaseModel):
|
||||
name: str
|
||||
|
||||
|
||||
class ContractTemplateUpdate(BaseModel):
|
||||
name: str
|
||||
|
||||
|
||||
class ClauseTemplateRead(ClauseTemplate):
|
||||
pass
|
||||
|
||||
|
||||
class ClauseTemplateCreate(BaseModel):
|
||||
name: str
|
||||
body: str
|
||||
|
||||
|
||||
class ClauseTemplateUpdate(BaseModel):
|
||||
name: str
|
||||
body: str
|
||||
3
back/app/user/__init__.py
Normal file
3
back/app/user/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .routes import router as user_router, get_auth_router
|
||||
|
||||
from .models import User, AccessToken
|
||||
BIN
back/app/user/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
back/app/user/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/user/__pycache__/manager.cpython-310.pyc
Normal file
BIN
back/app/user/__pycache__/manager.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/user/__pycache__/model.cpython-310.pyc
Normal file
BIN
back/app/user/__pycache__/model.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/user/__pycache__/models.cpython-310.pyc
Normal file
BIN
back/app/user/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/user/__pycache__/routes.cpython-310.pyc
Normal file
BIN
back/app/user/__pycache__/routes.cpython-310.pyc
Normal file
Binary file not shown.
BIN
back/app/user/__pycache__/schemas.cpython-310.pyc
Normal file
BIN
back/app/user/__pycache__/schemas.cpython-310.pyc
Normal file
Binary file not shown.
71
back/app/user/manager.py
Normal file
71
back/app/user/manager.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import uuid
|
||||
from typing import Any, Dict, Generic, Optional
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi_users import BaseUserManager, UUIDIDMixin, models, exceptions, schemas
|
||||
|
||||
from .models import User, get_user_db
|
||||
SECRET = "SECRET"
|
||||
|
||||
|
||||
class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
|
||||
reset_password_token_secret = SECRET
|
||||
verification_token_secret = SECRET
|
||||
|
||||
async def get_by_email(self, user_email: str) -> models.UP:
|
||||
return await self.get_by_login(user_email)
|
||||
|
||||
async def get_by_login(self, user_login: str) -> models.UP:
|
||||
"""
|
||||
Get a user by user_login.
|
||||
|
||||
:param user_login: E-mail of the user to retrieve.
|
||||
:raises UserNotExists: The user does not exist.
|
||||
:return: A user.
|
||||
"""
|
||||
user = await self.user_db.get_by_login(user_login)
|
||||
|
||||
if user is None:
|
||||
raise exceptions.UserNotExists()
|
||||
|
||||
return user
|
||||
|
||||
async def create(
|
||||
self,
|
||||
user_create: schemas.UC,
|
||||
safe: bool = False
|
||||
) -> models.UP:
|
||||
"""
|
||||
Create a user in database.
|
||||
|
||||
Triggers the on_after_register handler on success.
|
||||
|
||||
:param user_create: The UserCreate model to create.
|
||||
:param safe: If True, sensitive values like is_superuser or is_verified
|
||||
will be ignored during the creation, defaults to False.
|
||||
:raises UserAlreadyExists: A user already exists with the same e-mail.
|
||||
:return: A new user.
|
||||
"""
|
||||
await self.validate_password(user_create.password, user_create)
|
||||
|
||||
existing_user = await self.user_db.get_by_login(user_create.login)
|
||||
if existing_user is not None:
|
||||
raise exceptions.UserAlreadyExists()
|
||||
|
||||
user_dict = (
|
||||
user_create.create_update_dict()
|
||||
if safe
|
||||
else user_create.create_update_dict_superuser()
|
||||
)
|
||||
password = user_dict.pop("password")
|
||||
user_dict["hashed_password"] = self.password_helper.hash(password)
|
||||
|
||||
created_user = await self.user_db.create(user_dict)
|
||||
|
||||
await self.on_after_register(created_user)
|
||||
|
||||
return created_user
|
||||
|
||||
|
||||
async def get_user_manager(user_db=Depends(get_user_db)):
|
||||
yield UserManager(user_db)
|
||||
40
back/app/user/models.py
Normal file
40
back/app/user/models.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from typing import Optional, TypeVar
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel, Field
|
||||
from beanie import PydanticObjectId
|
||||
|
||||
from fastapi_users.db import BeanieBaseUser, BeanieUserDatabase
|
||||
from fastapi_users_db_beanie.access_token import BeanieAccessTokenDatabase, BeanieBaseAccessToken
|
||||
|
||||
from pymongo import IndexModel
|
||||
|
||||
|
||||
class AccessToken(BeanieBaseAccessToken[PydanticObjectId]):
|
||||
pass
|
||||
|
||||
|
||||
class User(BeanieBaseUser[PydanticObjectId]):
|
||||
login: str
|
||||
created_at: datetime = Field(default=datetime.utcnow(), nullable=False)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)
|
||||
|
||||
class Settings:
|
||||
indexes = [
|
||||
IndexModel("login", unique=True),
|
||||
]
|
||||
|
||||
|
||||
class UserDatabase(BeanieUserDatabase):
|
||||
async def get_by_login(self, login: str) -> Optional[TypeVar("UP_BEANIE", bound=BeanieBaseUser)]:
|
||||
"""Get a single user by email."""
|
||||
return await self.user_model.find_one(
|
||||
self.user_model.login == login
|
||||
)
|
||||
|
||||
|
||||
async def get_access_token_db():
|
||||
yield BeanieAccessTokenDatabase(AccessToken)
|
||||
|
||||
|
||||
async def get_user_db():
|
||||
yield UserDatabase(User)
|
||||
97
back/app/user/routes.py
Normal file
97
back/app/user/routes.py
Normal file
@@ -0,0 +1,97 @@
|
||||
import uuid
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Depends, Request
|
||||
from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models, exceptions
|
||||
from fastapi_users.authentication import BearerTransport, AuthenticationBackend
|
||||
from fastapi_users.authentication.strategy.db import AccessTokenDatabase, DatabaseStrategy
|
||||
|
||||
from beanie import PydanticObjectId
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from typing import List
|
||||
|
||||
from .models import User, AccessToken, get_user_db, get_access_token_db
|
||||
from .schemas import UserRead, UserUpdate, UserCreate
|
||||
from .manager import get_user_manager
|
||||
|
||||
|
||||
def get_database_strategy(
|
||||
access_token_db: AccessTokenDatabase[AccessToken] = Depends(get_access_token_db),
|
||||
) -> DatabaseStrategy:
|
||||
return DatabaseStrategy(access_token_db, lifetime_seconds=3600)
|
||||
|
||||
|
||||
bearer_transport = BearerTransport(tokenUrl="auth/jwt/login")
|
||||
|
||||
|
||||
auth_backend = AuthenticationBackend(
|
||||
name="db",
|
||||
transport=bearer_transport,
|
||||
get_strategy=get_database_strategy,
|
||||
)
|
||||
|
||||
|
||||
fastapi_users = FastAPIUsers[User, uuid.UUID](
|
||||
get_user_manager,
|
||||
[auth_backend],
|
||||
)
|
||||
|
||||
|
||||
def get_auth_router():
|
||||
return fastapi_users.get_auth_router(auth_backend)
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/", response_description="User added to the database")
|
||||
async def create(user: UserCreate, user_manager=Depends(get_user_manager)) -> dict:
|
||||
await user_manager.create(user, safe=True)
|
||||
return {"message": "User added successfully"}
|
||||
|
||||
|
||||
@router.get("/{id}", response_description="User record retrieved")
|
||||
async def read_id(id: PydanticObjectId) -> UserRead:
|
||||
user = await User.get(id)
|
||||
return user
|
||||
|
||||
|
||||
@router.get("/", response_model=List[UserRead], response_description="User records retrieved")
|
||||
async def read_list() -> List[UserRead]:
|
||||
users = await User.find_all().to_list()
|
||||
return users
|
||||
|
||||
|
||||
@router.put("/{id}", response_description="User record updated")
|
||||
async def update(id: PydanticObjectId, req: UserUpdate) -> UserRead:
|
||||
req = {k: v for k, v in req.dict().items() if v is not None}
|
||||
update_query = {"$set": {
|
||||
field: value for field, value in req.items()
|
||||
}}
|
||||
|
||||
user = await User.get(id)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Review record not found!"
|
||||
)
|
||||
|
||||
await user.update(update_query)
|
||||
return user
|
||||
|
||||
|
||||
@router.delete("/{id}", response_description="User record deleted from the database")
|
||||
async def delete(id: PydanticObjectId) -> dict:
|
||||
record = await User.get(id)
|
||||
|
||||
if not record:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Review record not found!"
|
||||
)
|
||||
|
||||
await record.delete()
|
||||
return {
|
||||
"message": "Record deleted successfully"
|
||||
}
|
||||
35
back/app/user/schemas.py
Normal file
35
back/app/user/schemas.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import uuid
|
||||
from typing import TypeVar
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from fastapi_users import schemas
|
||||
|
||||
from ..core.schemas import Reader
|
||||
from .models import User
|
||||
|
||||
|
||||
class UserBase(schemas.CreateUpdateDictModel):
|
||||
pass
|
||||
|
||||
|
||||
class UserRead(User):
|
||||
class Config:
|
||||
fields = {
|
||||
'_id': {'alias': 'id'},
|
||||
'hashed_password': {'exclude': True}
|
||||
}
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
login: str
|
||||
password: str
|
||||
email: str
|
||||
|
||||
|
||||
class UserUpdate(UserBase):
|
||||
pass
|
||||
|
||||
|
||||
class PasswordUpdate(BaseModel):
|
||||
old_password: str
|
||||
password: str
|
||||
3
back/debug.py
Normal file
3
back/debug.py
Normal file
@@ -0,0 +1,3 @@
|
||||
if __name__ == '__main__':
|
||||
import uvicorn
|
||||
uvicorn.run("app.main:app", host='0.0.0.0', port=8000, reload=True)
|
||||
5
back/requirements.txt
Normal file
5
back/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
fastapi==0.88.0
|
||||
fastapi_users==10.2.1
|
||||
fastapi_users_db_beanie==1.1.2
|
||||
motor==3.1.1
|
||||
uvicorn
|
||||
36
docker-compose.yml
Normal file
36
docker-compose.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
version: "3.9"
|
||||
services:
|
||||
back:
|
||||
build:
|
||||
context: ./back
|
||||
restart: always
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./back/app:/code/app
|
||||
|
||||
front:
|
||||
build:
|
||||
context: ./front
|
||||
restart: always
|
||||
ports:
|
||||
- "4200:4200"
|
||||
volumes:
|
||||
- ./front/app/src:/app/src
|
||||
- ./front/app/public:/app/public
|
||||
|
||||
nginx:
|
||||
build:
|
||||
context: ./nginx
|
||||
restart: always
|
||||
ports:
|
||||
- "80:80"
|
||||
|
||||
mongo:
|
||||
image: "mongo"
|
||||
restart: always
|
||||
ports:
|
||||
- "27017:27017"
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: root
|
||||
MONGO_INITDB_ROOT_PASSWORD: example
|
||||
23
front/Dockerfile
Normal file
23
front/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM node:lts-alpine
|
||||
|
||||
# install simple http server for serving static content
|
||||
RUN npm install -g http-server
|
||||
|
||||
# make the 'app' folder the current working directory
|
||||
WORKDIR /app
|
||||
|
||||
# copy both 'package.json' and 'package-lock.json' (if available)
|
||||
COPY app/package*.json ./
|
||||
|
||||
# install project dependencies
|
||||
RUN npm install
|
||||
|
||||
# copy project files and folders to the current working directory (i.e. 'app' folder)
|
||||
COPY app/ .
|
||||
|
||||
# build app for production with minification
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 4200
|
||||
|
||||
CMD [ "npm", "run", "ng", "serve", "--", "--host", "0.0.0.0" ]
|
||||
16
front/app/.editorconfig
Normal file
16
front/app/.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
42
front/app/.gitignore
vendored
Normal file
42
front/app/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
4
front/app/.vscode/extensions.json
vendored
Normal file
4
front/app/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
||||
20
front/app/.vscode/launch.json
vendored
Normal file
20
front/app/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
front/app/.vscode/tasks.json
vendored
Normal file
42
front/app/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
27
front/app/README.md
Normal file
27
front/app/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# App
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.0.5.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
99
front/app/angular.json
Normal file
99
front/app/angular.json
Normal file
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"app": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/app",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "app:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "app:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "app:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11231
front/app/package-lock.json
generated
Normal file
11231
front/app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
42
front/app/package.json
Normal file
42
front/app/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.0",
|
||||
"@angular/common": "^15.0.0",
|
||||
"@angular/compiler": "^15.0.0",
|
||||
"@angular/core": "^15.0.0",
|
||||
"@angular/forms": "^15.0.0",
|
||||
"@angular/platform-browser": "^15.0.0",
|
||||
"@angular/platform-browser-dynamic": "^15.0.0",
|
||||
"@angular/router": "^15.0.0",
|
||||
"@angular/cli": "~15.0.5",
|
||||
"@ng-bootstrap/ng-bootstrap": "^14.0.0",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"bootstrap": "^5.2.3",
|
||||
"rxjs": "~7.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.0.5",
|
||||
"@angular/compiler-cli": "^15.0.0",
|
||||
"@angular/localize": "^15.0.0",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"jasmine-core": "~4.5.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"typescript": "~4.8.2"
|
||||
}
|
||||
}
|
||||
10
front/app/src/app/app-routing.module.ts
Normal file
10
front/app/src/app/app-routing.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
const routes: Routes = [];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
0
front/app/src/app/app.component.css
Normal file
0
front/app/src/app/app.component.css
Normal file
3
front/app/src/app/app.component.html
Normal file
3
front/app/src/app/app.component.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<app-auth></app-auth>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
35
front/app/src/app/app.component.spec.ts
Normal file
35
front/app/src/app/app.component.spec.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'app'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('app');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('.content span')?.textContent).toContain('app app is running!');
|
||||
});
|
||||
});
|
||||
10
front/app/src/app/app.component.ts
Normal file
10
front/app/src/app/app.component.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'app';
|
||||
}
|
||||
22
front/app/src/app/app.module.ts
Normal file
22
front/app/src/app/app.module.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { AuthComponent } from './auth/auth.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
AuthComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
NgbModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
0
front/app/src/app/auth/auth.component.css
Normal file
0
front/app/src/app/auth/auth.component.css
Normal file
1
front/app/src/app/auth/auth.component.html
Normal file
1
front/app/src/app/auth/auth.component.html
Normal file
@@ -0,0 +1 @@
|
||||
<p>auth works!</p>
|
||||
23
front/app/src/app/auth/auth.component.spec.ts
Normal file
23
front/app/src/app/auth/auth.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AuthComponent } from './auth.component';
|
||||
|
||||
describe('AuthComponent', () => {
|
||||
let component: AuthComponent;
|
||||
let fixture: ComponentFixture<AuthComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AuthComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AuthComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
10
front/app/src/app/auth/auth.component.ts
Normal file
10
front/app/src/app/auth/auth.component.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-auth',
|
||||
templateUrl: './auth.component.html',
|
||||
styleUrls: ['./auth.component.css']
|
||||
})
|
||||
export class AuthComponent {
|
||||
|
||||
}
|
||||
0
front/app/src/assets/.gitkeep
Normal file
0
front/app/src/assets/.gitkeep
Normal file
BIN
front/app/src/favicon.ico
Normal file
BIN
front/app/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 948 B |
13
front/app/src/index.html
Normal file
13
front/app/src/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>App</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
7
front/app/src/main.ts
Normal file
7
front/app/src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
1
front/app/src/styles.css
Normal file
1
front/app/src/styles.css
Normal file
@@ -0,0 +1 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
16
front/app/tsconfig.app.json
Normal file
16
front/app/tsconfig.app.json
Normal file
@@ -0,0 +1,16 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": [
|
||||
"@angular/localize"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
36
front/app/tsconfig.json
Normal file
36
front/app/tsconfig.json
Normal file
@@ -0,0 +1,36 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"useDefineForClassFields": false,
|
||||
"types": [
|
||||
"@angular/localize"
|
||||
],
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
15
front/app/tsconfig.spec.json
Normal file
15
front/app/tsconfig.spec.json
Normal file
@@ -0,0 +1,15 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"@angular/localize"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
3
nginx/Dockerfile
Normal file
3
nginx/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
38
nginx/nginx.conf
Normal file
38
nginx/nginx.conf
Normal file
@@ -0,0 +1,38 @@
|
||||
worker_processes 1;
|
||||
|
||||
events { worker_connections 1024; }
|
||||
|
||||
http {
|
||||
|
||||
sendfile on;
|
||||
|
||||
upstream docker-front {
|
||||
server front:4200;
|
||||
}
|
||||
|
||||
upstream docker-back {
|
||||
server back:8000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
proxy_pass http://docker-front;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Host $server_name;
|
||||
}
|
||||
|
||||
location /api/v1/ {
|
||||
proxy_pass http://docker-back/;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Host $server_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user