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