initial commit

This commit is contained in:
2023-01-09 13:03:16 +01:00
commit d0c0668fad
89 changed files with 12472 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.idea/
__pycache__

16
back/Dockerfile Normal file
View 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
View File

View File

@@ -0,0 +1 @@
from .routes import router as contract_router

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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)

View 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)

View 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

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

64
back/app/core/routes.py Normal file
View 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
View 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
View 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, ], )

View File

@@ -0,0 +1 @@
from .routes import router as entity_router

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

20
back/app/entity/models.py Normal file
View 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)

View 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)

View 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
View 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)

View File

@@ -0,0 +1 @@
from .routes import router as order_router

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

13
back/app/order/models.py Normal file
View 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
View 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
View File

@@ -0,0 +1,14 @@
import uuid
from pydantic import BaseModel
class OrderRead(BaseModel):
pass
class OrderCreate(BaseModel):
login: str
class OrderUpdate(BaseModel):
pass

View 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"], )

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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)

View 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)

View 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)

View 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

View File

@@ -0,0 +1,3 @@
from .routes import router as user_router, get_auth_router
from .models import User, AccessToken

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

71
back/app/user/manager.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

42
front/app/package.json Normal file
View 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"
}
}

View 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 { }

View File

View File

@@ -0,0 +1,3 @@
<app-auth></app-auth>
<router-outlet></router-outlet>

View 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!');
});
});

View 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';
}

View 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 { }

View File

@@ -0,0 +1 @@
<p>auth works!</p>

View 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();
});
});

View 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 {
}

View 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
View 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
View 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
View File

@@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

View 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
View 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
}
}

View 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
View File

@@ -0,0 +1,3 @@
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf

38
nginx/nginx.conf Normal file
View 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;
}
}
}