Refactor/upgrade backend and frontend parts (#2)
* ♻️ Refactor and simplify backend code * ♻️ Refactor frontend state, integrate typesafe-vuex accessors into state files * ♻️ Use new state accessors and standardize layout * 🔒 Upgrade and fix npm security audit * 🔧 Update local re-generation scripts * 🔊 Log startup exceptions to detect errors early * ✏️ Fix password reset token content * 🔥 Remove unneeded Dockerfile directives * 🔥 Remove unnecessary print * 🔥 Remove unnecessary code, upgrade dependencies in backend * ✏️ Fix typos in docstrings and comments * 🏗️ Improve user Depends utilities to simplify and remove code * 🔥 Remove deprecated SQLAlchemy parameter
This commit is contained in:
committed by
GitHub
parent
9e0b826618
commit
cd112bd683
17
dev-fsfp-back.sh
Normal file
17
dev-fsfp-back.sh
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
# Run this script from outside the project, to integrate a dev-fsfp project with changes and review modifications
|
||||||
|
|
||||||
|
# Exit in case of error
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ $(uname -s) = "Linux" ]; then
|
||||||
|
echo "Remove __pycache__ files"
|
||||||
|
sudo find ./dev-fsfp/ -type d -name __pycache__ -exec rm -r {} \+
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf ./full-stack-fastapi-postgresql/\{\{cookiecutter.project_slug\}\}/*
|
||||||
|
|
||||||
|
rsync -a --exclude=node_modules ./dev-fsfp/* ./full-stack-fastapi-postgresql/\{\{cookiecutter.project_slug\}\}/
|
||||||
|
|
||||||
|
rsync -a ./dev-fsfp/{.env,.gitignore,.gitlab-ci.yml} ./full-stack-fastapi-postgresql/\{\{cookiecutter.project_slug\}\}/
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#! /usr/bin/env bash
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
# Run this script from outside the project, to generate a dev-fsfp project
|
||||||
|
|
||||||
# Exit in case of error
|
# Exit in case of error
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ The changes to those files only affect the local development environment, not th
|
|||||||
|
|
||||||
For example, the directory with the backend code is mounted as a Docker "host volume" (in the file `docker-compose.dev.volumes.yml`), mapping the code you change live to the directory inside the container. That allows you to test your changes right away, without having to build the Docker image again. It should only be done during development, for production, you should build the Docker image with a recent version of the backend code. But during development, it allows you to iterate very fast.
|
For example, the directory with the backend code is mounted as a Docker "host volume" (in the file `docker-compose.dev.volumes.yml`), mapping the code you change live to the directory inside the container. That allows you to test your changes right away, without having to build the Docker image again. It should only be done during development, for production, you should build the Docker image with a recent version of the backend code. But during development, it allows you to iterate very fast.
|
||||||
|
|
||||||
There is also a commented out `command` override (in the file `docker-compose.dev.command.yml`), if you want to enable it, uncomment it. It makes the backend container run a process that does "nothing", but keeps the process running. That allows you to get inside your living container and run commands inside, for example a Python interpreter to test installed dependencies, or start the development server that reloads when it detectes changes.
|
There is also a commented out `command` override (in the file `docker-compose.dev.command.yml`), if you want to enable it, uncomment it. It makes the backend container run a process that does "nothing", but keeps the process running. That allows you to get inside your living container and run commands inside, for example a Python interpreter to test installed dependencies, or start the development server that reloads when it detects changes.
|
||||||
|
|
||||||
To get inside the container with a `bash` session you can start the stack with:
|
To get inside the container with a `bash` session you can start the stack with:
|
||||||
|
|
||||||
@@ -91,16 +91,16 @@ root@7f2607af31c3:/app#
|
|||||||
|
|
||||||
that means that you are in a `bash` session inside your container, as a `root` user, under the `/app` directory.
|
that means that you are in a `bash` session inside your container, as a `root` user, under the `/app` directory.
|
||||||
|
|
||||||
There is also a script `backend-live.sh` to run the debug live reloading server. You can run that script from inside the container with:
|
There is also a script `/start-reload.sh` to run the debug live reloading server. You can run that script from inside the container with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash ./backend-live.sh
|
bash /start-reload.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
...it will look like:
|
...it will look like:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
root@7f2607af31c3:/app# bash ./backend-live.sh
|
root@7f2607af31c3:/app# bash /start-reload.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
and then hit enter. That runs the debugging server that auto reloads when it detects code changes.
|
and then hit enter. That runs the debugging server that auto reloads when it detects code changes.
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.api.api_v1.endpoints.token import router as token_router
|
from app.api.api_v1.endpoints import token, user, utils
|
||||||
from app.api.api_v1.endpoints.user import router as user_router
|
|
||||||
from app.api.api_v1.endpoints.utils import router as utils_router
|
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
api_router.include_router(token_router)
|
api_router.include_router(token.router)
|
||||||
api_router.include_router(user_router)
|
api_router.include_router(user.router)
|
||||||
api_router.include_router(utils_router)
|
api_router.include_router(utils.router)
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ from fastapi import APIRouter, Depends, HTTPException
|
|||||||
from fastapi.security import OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app import crud
|
||||||
from app.api.utils.db import get_db
|
from app.api.utils.db import get_db
|
||||||
from app.api.utils.security import get_current_user
|
from app.api.utils.security import get_current_user
|
||||||
from app.core import config
|
from app.core import config
|
||||||
from app.core.jwt import create_access_token
|
from app.core.jwt import create_access_token
|
||||||
from app.core.security import get_password_hash
|
from app.core.security import get_password_hash
|
||||||
from app.crud import user as crud_user
|
|
||||||
from app.db_models.user import User as DBUser
|
from app.db_models.user import User as DBUser
|
||||||
from app.models.msg import Msg
|
from app.models.msg import Msg
|
||||||
from app.models.token import Token
|
from app.models.token import Token
|
||||||
@@ -30,12 +30,12 @@ def login_access_token(
|
|||||||
"""
|
"""
|
||||||
OAuth2 compatible token login, get an access token for future requests
|
OAuth2 compatible token login, get an access token for future requests
|
||||||
"""
|
"""
|
||||||
user = crud_user.authenticate(
|
user = crud.user.authenticate(
|
||||||
db, email=form_data.username, password=form_data.password
|
db, email=form_data.username, password=form_data.password
|
||||||
)
|
)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=400, detail="Incorrect email or password")
|
raise HTTPException(status_code=400, detail="Incorrect email or password")
|
||||||
elif not crud_user.is_active(user):
|
elif not crud.user.is_active(user):
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
raise HTTPException(status_code=400, detail="Inactive user")
|
||||||
access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES)
|
access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
return {
|
return {
|
||||||
@@ -59,7 +59,7 @@ def recover_password(email: str, db: Session = Depends(get_db)):
|
|||||||
"""
|
"""
|
||||||
Password Recovery
|
Password Recovery
|
||||||
"""
|
"""
|
||||||
user = crud_user.get_by_email(db, email=email)
|
user = crud.user.get_by_email(db, email=email)
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -81,13 +81,13 @@ def reset_password(token: str, new_password: str, db: Session = Depends(get_db))
|
|||||||
email = verify_password_reset_token(token)
|
email = verify_password_reset_token(token)
|
||||||
if not email:
|
if not email:
|
||||||
raise HTTPException(status_code=400, detail="Invalid token")
|
raise HTTPException(status_code=400, detail="Invalid token")
|
||||||
user = crud_user.get_by_email(db, email=email)
|
user = crud.user.get_by_email(db, email=email)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=404,
|
status_code=404,
|
||||||
detail="The user with this username does not exist in the system.",
|
detail="The user with this username does not exist in the system.",
|
||||||
)
|
)
|
||||||
elif not crud_user.is_active(user):
|
elif not crud.user.is_active(user):
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
raise HTTPException(status_code=400, detail="Inactive user")
|
||||||
hashed_password = get_password_hash(new_password)
|
hashed_password = get_password_hash(new_password)
|
||||||
user.hashed_password = hashed_password
|
user.hashed_password = hashed_password
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ from fastapi.encoders import jsonable_encoder
|
|||||||
from pydantic.types import EmailStr
|
from pydantic.types import EmailStr
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app import crud
|
||||||
from app.api.utils.db import get_db
|
from app.api.utils.db import get_db
|
||||||
from app.api.utils.security import get_current_user
|
from app.api.utils.security import get_current_active_superuser, get_current_active_user
|
||||||
from app.core import config
|
from app.core import config
|
||||||
from app.crud import user as crud_user
|
|
||||||
from app.db_models.user import User as DBUser
|
from app.db_models.user import User as DBUser
|
||||||
from app.models.user import User, UserInCreate, UserInDB, UserInUpdate
|
from app.models.user import User, UserInCreate, UserInDB, UserInUpdate
|
||||||
from app.utils import send_new_account_email
|
from app.utils import send_new_account_email
|
||||||
@@ -21,18 +21,12 @@ def read_users(
|
|||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
current_user: DBUser = Depends(get_current_user),
|
current_user: DBUser = Depends(get_current_active_superuser),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Retrieve users
|
Retrieve users
|
||||||
"""
|
"""
|
||||||
if not crud_user.is_active(current_user):
|
users = crud.user.get_multi(db, skip=skip, limit=limit)
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
|
||||||
elif not crud_user.is_superuser(current_user):
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400, detail="The user doesn't have enough privileges"
|
|
||||||
)
|
|
||||||
users = crud_user.get_multi(db, skip=skip, limit=limit)
|
|
||||||
return users
|
return users
|
||||||
|
|
||||||
|
|
||||||
@@ -41,24 +35,18 @@ def create_user(
|
|||||||
*,
|
*,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
user_in: UserInCreate,
|
user_in: UserInCreate,
|
||||||
current_user: DBUser = Depends(get_current_user),
|
current_user: DBUser = Depends(get_current_active_superuser),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Create new user
|
Create new user
|
||||||
"""
|
"""
|
||||||
if not crud_user.is_active(current_user):
|
user = crud.user.get_by_email(db, email=user_in.email)
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
|
||||||
elif not crud_user.is_superuser(current_user):
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400, detail="The user doesn't have enough privileges"
|
|
||||||
)
|
|
||||||
user = crud_user.get_by_email(db, email=user_in.email)
|
|
||||||
if user:
|
if user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
detail="The user with this username already exists in the system.",
|
detail="The user with this username already exists in the system.",
|
||||||
)
|
)
|
||||||
user = crud_user.create(db, user_in=user_in)
|
user = crud.user.create(db, user_in=user_in)
|
||||||
if config.EMAILS_ENABLED and user_in.email:
|
if config.EMAILS_ENABLED and user_in.email:
|
||||||
send_new_account_email(
|
send_new_account_email(
|
||||||
email_to=user_in.email, username=user_in.email, password=user_in.password
|
email_to=user_in.email, username=user_in.email, password=user_in.password
|
||||||
@@ -73,13 +61,11 @@ def update_user_me(
|
|||||||
password: str = Body(None),
|
password: str = Body(None),
|
||||||
full_name: str = Body(None),
|
full_name: str = Body(None),
|
||||||
email: EmailStr = Body(None),
|
email: EmailStr = Body(None),
|
||||||
current_user: DBUser = Depends(get_current_user),
|
current_user: DBUser = Depends(get_current_active_user),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Update own user
|
Update own user
|
||||||
"""
|
"""
|
||||||
if not crud_user.is_active(current_user):
|
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
|
||||||
current_user_data = jsonable_encoder(current_user)
|
current_user_data = jsonable_encoder(current_user)
|
||||||
user_in = UserInUpdate(**current_user_data)
|
user_in = UserInUpdate(**current_user_data)
|
||||||
if password is not None:
|
if password is not None:
|
||||||
@@ -88,19 +74,18 @@ def update_user_me(
|
|||||||
user_in.full_name = full_name
|
user_in.full_name = full_name
|
||||||
if email is not None:
|
if email is not None:
|
||||||
user_in.email = email
|
user_in.email = email
|
||||||
user = crud_user.update(db, user=current_user, user_in=user_in)
|
user = crud.user.update(db, user=current_user, user_in=user_in)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
@router.get("/users/me", tags=["users"], response_model=User)
|
@router.get("/users/me", tags=["users"], response_model=User)
|
||||||
def read_user_me(
|
def read_user_me(
|
||||||
db: Session = Depends(get_db), current_user: DBUser = Depends(get_current_user)
|
db: Session = Depends(get_db),
|
||||||
|
current_user: DBUser = Depends(get_current_active_user),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Get current user
|
Get current user
|
||||||
"""
|
"""
|
||||||
if not crud_user.is_active(current_user):
|
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
|
||||||
return current_user
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
@@ -120,32 +105,30 @@ def create_user_open(
|
|||||||
status_code=403,
|
status_code=403,
|
||||||
detail="Open user resgistration is forbidden on this server",
|
detail="Open user resgistration is forbidden on this server",
|
||||||
)
|
)
|
||||||
user = crud_user.get_by_email(db, email=email)
|
user = crud.user.get_by_email(db, email=email)
|
||||||
if user:
|
if user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
detail="The user with this username already exists in the system",
|
detail="The user with this username already exists in the system",
|
||||||
)
|
)
|
||||||
user_in = UserInCreate(password=password, email=email, full_name=full_name)
|
user_in = UserInCreate(password=password, email=email, full_name=full_name)
|
||||||
user = crud_user.create(db, user_in=user_in)
|
user = crud.user.create(db, user_in=user_in)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
@router.get("/users/{user_id}", tags=["users"], response_model=User)
|
@router.get("/users/{user_id}", tags=["users"], response_model=User)
|
||||||
def read_user_by_id(
|
def read_user_by_id(
|
||||||
user_id: int,
|
user_id: int,
|
||||||
current_user: DBUser = Depends(get_current_user),
|
current_user: DBUser = Depends(get_current_active_user),
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Get a specific user by username (email)
|
Get a specific user by username (email)
|
||||||
"""
|
"""
|
||||||
if not crud_user.is_active(current_user):
|
user = crud.user.get(db, user_id=user_id)
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
|
||||||
user = crud_user.get(db, user_id=user_id)
|
|
||||||
if user == current_user:
|
if user == current_user:
|
||||||
return user
|
return user
|
||||||
if not crud_user.is_superuser(current_user):
|
if not crud.user.is_superuser(current_user):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400, detail="The user doesn't have enough privileges"
|
status_code=400, detail="The user doesn't have enough privileges"
|
||||||
)
|
)
|
||||||
@@ -158,23 +141,17 @@ def update_user(
|
|||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
user_id: int,
|
user_id: int,
|
||||||
user_in: UserInUpdate,
|
user_in: UserInUpdate,
|
||||||
current_user: UserInDB = Depends(get_current_user),
|
current_user: UserInDB = Depends(get_current_active_superuser),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Update a user
|
Update a user
|
||||||
"""
|
"""
|
||||||
if not crud_user.is_active(current_user):
|
user = crud.user.get(db, user_id=user_id)
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
|
||||||
elif not crud_user.is_superuser(current_user):
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400, detail="The user doesn't have enough privileges"
|
|
||||||
)
|
|
||||||
user = crud_user.get(db, user_id=user_id)
|
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=404,
|
status_code=404,
|
||||||
detail="The user with this username does not exist in the system",
|
detail="The user with this username does not exist in the system",
|
||||||
)
|
)
|
||||||
user = crud_user.update(db, user=user, user_in=user_in)
|
user = crud.user.update(db, user=user, user_in=user_in)
|
||||||
return user
|
return user
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends
|
||||||
from pydantic.types import EmailStr
|
from pydantic.types import EmailStr
|
||||||
|
|
||||||
from app.api.utils.security import get_current_user
|
from app.api.utils.security import get_current_active_superuser
|
||||||
from app.core.celery_app import celery_app
|
from app.core.celery_app import celery_app
|
||||||
from app.crud import user as crud_user
|
|
||||||
from app.models.msg import Msg
|
from app.models.msg import Msg
|
||||||
from app.models.user import UserInDB
|
from app.models.user import UserInDB
|
||||||
from app.utils import send_test_email
|
from app.utils import send_test_email
|
||||||
@@ -12,22 +11,22 @@ router = APIRouter()
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/test-celery/", tags=["utils"], response_model=Msg, status_code=201)
|
@router.post("/test-celery/", tags=["utils"], response_model=Msg, status_code=201)
|
||||||
def test_celery(msg: Msg, current_user: UserInDB = Depends(get_current_user)):
|
def test_celery(
|
||||||
|
msg: Msg, current_user: UserInDB = Depends(get_current_active_superuser)
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Test Celery worker
|
Test Celery worker
|
||||||
"""
|
"""
|
||||||
if not crud_user.is_superuser(current_user):
|
|
||||||
raise HTTPException(status_code=400, detail="Not a superuser")
|
|
||||||
celery_app.send_task("app.worker.test_celery", args=[msg.msg])
|
celery_app.send_task("app.worker.test_celery", args=[msg.msg])
|
||||||
return {"msg": "Word received"}
|
return {"msg": "Word received"}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/test-email/", tags=["utils"], response_model=Msg, status_code=201)
|
@router.post("/test-email/", tags=["utils"], response_model=Msg, status_code=201)
|
||||||
def test_email(email_to: EmailStr, current_user: UserInDB = Depends(get_current_user)):
|
def test_email(
|
||||||
|
email_to: EmailStr, current_user: UserInDB = Depends(get_current_active_superuser)
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Test emails
|
Test emails
|
||||||
"""
|
"""
|
||||||
if not crud_user.is_superuser(current_user):
|
|
||||||
raise HTTPException(status_code=400, detail="Not a superuser")
|
|
||||||
send_test_email(email_to=email_to)
|
send_test_email(email_to=email_to)
|
||||||
return {"msg": "Test email sent"}
|
return {"msg": "Test email sent"}
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ from jwt import PyJWTError
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from starlette.status import HTTP_403_FORBIDDEN
|
from starlette.status import HTTP_403_FORBIDDEN
|
||||||
|
|
||||||
|
from app import crud
|
||||||
from app.api.utils.db import get_db
|
from app.api.utils.db import get_db
|
||||||
from app.core import config
|
from app.core import config
|
||||||
from app.core.jwt import ALGORITHM
|
from app.core.jwt import ALGORITHM
|
||||||
from app.crud import user as crud_user
|
from app.db_models.user import User
|
||||||
from app.models.token import TokenPayload
|
from app.models.token import TokenPayload
|
||||||
|
|
||||||
reusable_oauth2 = OAuth2PasswordBearer(tokenUrl="/api/v1/login/access-token")
|
reusable_oauth2 = OAuth2PasswordBearer(tokenUrl="/api/v1/login/access-token")
|
||||||
@@ -24,7 +25,21 @@ def get_current_user(
|
|||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
|
status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
|
||||||
)
|
)
|
||||||
user = crud_user.get(db, user_id=token_data.user_id)
|
user = crud.user.get(db, user_id=token_data.user_id)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="User not found")
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_active_user(current_user: User = Security(get_current_user)):
|
||||||
|
if not crud.user.is_active(current_user):
|
||||||
|
raise HTTPException(status_code=400, detail="Inactive user")
|
||||||
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_active_superuser(current_user: User = Security(get_current_user)):
|
||||||
|
if not crud.user.is_superuser(current_user):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400, detail="The user doesn't have enough privileges"
|
||||||
|
)
|
||||||
|
return current_user
|
||||||
|
|||||||
@@ -18,8 +18,12 @@ wait_seconds = 1
|
|||||||
after=after_log(logger, logging.WARN),
|
after=after_log(logger, logging.WARN),
|
||||||
)
|
)
|
||||||
def init():
|
def init():
|
||||||
# Try to create session to check if DB is awake
|
try:
|
||||||
db_session.execute("SELECT 1")
|
# Try to create session to check if DB is awake
|
||||||
|
db_session.execute("SELECT 1")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -18,8 +18,12 @@ wait_seconds = 1
|
|||||||
after=after_log(logger, logging.WARN),
|
after=after_log(logger, logging.WARN),
|
||||||
)
|
)
|
||||||
def init():
|
def init():
|
||||||
# Try to create session to check if DB is awake
|
try:
|
||||||
db_session.execute("SELECT 1")
|
# Try to create session to check if DB is awake
|
||||||
|
db_session.execute("SELECT 1")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
from . import user
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import List, Union
|
from typing import List, Optional
|
||||||
|
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
|
||||||
@@ -7,20 +7,20 @@ from app.db_models.user import User
|
|||||||
from app.models.user import UserInCreate, UserInUpdate
|
from app.models.user import UserInCreate, UserInUpdate
|
||||||
|
|
||||||
|
|
||||||
def get(db_session, *, user_id: int) -> Union[User, None]:
|
def get(db_session, *, user_id: int) -> Optional[User]:
|
||||||
return db_session.query(User).filter(User.id == user_id).first()
|
return db_session.query(User).filter(User.id == user_id).first()
|
||||||
|
|
||||||
|
|
||||||
def get_by_email(db_session, *, email: str) -> Union[User, None]:
|
def get_by_email(db_session, *, email: str) -> Optional[User]:
|
||||||
return db_session.query(User).filter(User.email == email).first()
|
return db_session.query(User).filter(User.email == email).first()
|
||||||
|
|
||||||
|
|
||||||
def authenticate(db_session, *, email: str, password: str) -> Union[User, bool]:
|
def authenticate(db_session, *, email: str, password: str) -> Optional[User]:
|
||||||
user = get_by_email(db_session, email=email)
|
user = get_by_email(db_session, email=email)
|
||||||
if not user:
|
if not user:
|
||||||
return False
|
return None
|
||||||
if not verify_password(password, user.hashed_password):
|
if not verify_password(password, user.hashed_password):
|
||||||
return False
|
return None
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ def is_superuser(user) -> bool:
|
|||||||
return user.is_superuser
|
return user.is_superuser
|
||||||
|
|
||||||
|
|
||||||
def get_multi(db_session, *, skip=0, limit=100) -> Union[List[User], List[None]]:
|
def get_multi(db_session, *, skip=0, limit=100) -> List[Optional[User]]:
|
||||||
return db_session.query(User).offset(skip).limit(limit).all()
|
return db_session.query(User).offset(skip).limit(limit).all()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
|
from app import crud
|
||||||
from app.core import config
|
from app.core import config
|
||||||
from app.crud import user as crud_user
|
|
||||||
from app.models.user import UserInCreate
|
from app.models.user import UserInCreate
|
||||||
|
|
||||||
|
|
||||||
def init_db(db_session):
|
def init_db(db_session):
|
||||||
# Tables should be created with Alembic migrations
|
# Tables should be created with Alembic migrations
|
||||||
# But if you don't want to use migrations, create
|
# But if you don't want to use migrations, create
|
||||||
# the tables uncommenting the next line
|
# the tables un-commenting the next line
|
||||||
# Base.metadata.create_all(bind=engine)
|
# Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
user = crud_user.get_by_email(db_session, email=config.FIRST_SUPERUSER)
|
user = crud.user.get_by_email(db_session, email=config.FIRST_SUPERUSER)
|
||||||
if not user:
|
if not user:
|
||||||
user_in = UserInCreate(
|
user_in = UserInCreate(
|
||||||
email=config.FIRST_SUPERUSER,
|
email=config.FIRST_SUPERUSER,
|
||||||
password=config.FIRST_SUPERUSER_PASSWORD,
|
password=config.FIRST_SUPERUSER_PASSWORD,
|
||||||
is_superuser=True,
|
is_superuser=True,
|
||||||
)
|
)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from sqlalchemy.orm import scoped_session, sessionmaker
|
|||||||
|
|
||||||
from app.core import config
|
from app.core import config
|
||||||
|
|
||||||
engine = create_engine(config.SQLALCHEMY_DATABASE_URI, convert_unicode=True)
|
engine = create_engine(config.SQLALCHEMY_DATABASE_URI)
|
||||||
db_session = scoped_session(
|
db_session = scoped_session(
|
||||||
sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from app import crud
|
||||||
from app.core import config
|
from app.core import config
|
||||||
from app.crud import user as crud_user
|
|
||||||
from app.db.session import db_session
|
from app.db.session import db_session
|
||||||
from app.models.user import UserInCreate
|
from app.models.user import UserInCreate
|
||||||
from app.tests.utils.user import user_authentication_headers
|
from app.tests.utils.user import user_authentication_headers
|
||||||
@@ -32,7 +32,7 @@ def test_create_user_new_email(superuser_token_headers):
|
|||||||
)
|
)
|
||||||
assert 200 <= r.status_code < 300
|
assert 200 <= r.status_code < 300
|
||||||
created_user = r.json()
|
created_user = r.json()
|
||||||
user = crud_user.get_by_email(db_session, email=username)
|
user = crud.user.get_by_email(db_session, email=username)
|
||||||
assert user.email == created_user["email"]
|
assert user.email == created_user["email"]
|
||||||
|
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ def test_get_existing_user(superuser_token_headers):
|
|||||||
username = random_lower_string()
|
username = random_lower_string()
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=username, password=password)
|
user_in = UserInCreate(email=username, password=password)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
user_id = user.id
|
user_id = user.id
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
f"{server_api}{config.API_V1_STR}/users/{user_id}",
|
f"{server_api}{config.API_V1_STR}/users/{user_id}",
|
||||||
@@ -49,7 +49,7 @@ def test_get_existing_user(superuser_token_headers):
|
|||||||
)
|
)
|
||||||
assert 200 <= r.status_code < 300
|
assert 200 <= r.status_code < 300
|
||||||
api_user = r.json()
|
api_user = r.json()
|
||||||
user = crud_user.get_by_email(db_session, email=username)
|
user = crud.user.get_by_email(db_session, email=username)
|
||||||
assert user.email == api_user["email"]
|
assert user.email == api_user["email"]
|
||||||
|
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ def test_create_user_existing_username(superuser_token_headers):
|
|||||||
# username = email
|
# username = email
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=username, password=password)
|
user_in = UserInCreate(email=username, password=password)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
data = {"email": username, "password": password}
|
data = {"email": username, "password": password}
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
f"{server_api}{config.API_V1_STR}/users/",
|
f"{server_api}{config.API_V1_STR}/users/",
|
||||||
@@ -76,7 +76,7 @@ def test_create_user_by_normal_user():
|
|||||||
username = random_lower_string()
|
username = random_lower_string()
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=username, password=password)
|
user_in = UserInCreate(email=username, password=password)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
user_token_headers = user_authentication_headers(server_api, username, password)
|
user_token_headers = user_authentication_headers(server_api, username, password)
|
||||||
data = {"email": username, "password": password}
|
data = {"email": username, "password": password}
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
@@ -90,12 +90,12 @@ def test_retrieve_users(superuser_token_headers):
|
|||||||
username = random_lower_string()
|
username = random_lower_string()
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=username, password=password)
|
user_in = UserInCreate(email=username, password=password)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
|
|
||||||
username2 = random_lower_string()
|
username2 = random_lower_string()
|
||||||
password2 = random_lower_string()
|
password2 = random_lower_string()
|
||||||
user_in2 = UserInCreate(email=username2, password=password2)
|
user_in2 = UserInCreate(email=username2, password=password2)
|
||||||
user2 = crud_user.create(db_session, user_in=user_in2)
|
user2 = crud.user.create(db_session, user_in=user_in2)
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
f"{server_api}{config.API_V1_STR}/users/", headers=superuser_token_headers
|
f"{server_api}{config.API_V1_STR}/users/", headers=superuser_token_headers
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
|
||||||
from app.crud import user as crud_user
|
from app import crud
|
||||||
from app.db.session import db_session
|
from app.db.session import db_session
|
||||||
from app.models.user import UserInCreate
|
from app.models.user import UserInCreate
|
||||||
from app.tests.utils.utils import random_lower_string
|
from app.tests.utils.utils import random_lower_string
|
||||||
@@ -10,7 +10,7 @@ def test_create_user():
|
|||||||
email = random_lower_string()
|
email = random_lower_string()
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=email, password=password)
|
user_in = UserInCreate(email=email, password=password)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
assert user.email == email
|
assert user.email == email
|
||||||
assert hasattr(user, "hashed_password")
|
assert hasattr(user, "hashed_password")
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ def test_authenticate_user():
|
|||||||
email = random_lower_string()
|
email = random_lower_string()
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=email, password=password)
|
user_in = UserInCreate(email=email, password=password)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
authenticated_user = crud_user.authenticate(
|
authenticated_user = crud.user.authenticate(
|
||||||
db_session, email=email, password=password
|
db_session, email=email, password=password
|
||||||
)
|
)
|
||||||
assert authenticated_user
|
assert authenticated_user
|
||||||
@@ -30,16 +30,16 @@ def test_authenticate_user():
|
|||||||
def test_not_authenticate_user():
|
def test_not_authenticate_user():
|
||||||
email = random_lower_string()
|
email = random_lower_string()
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user = crud_user.authenticate(db_session, email=email, password=password)
|
user = crud.user.authenticate(db_session, email=email, password=password)
|
||||||
assert user is False
|
assert user is None
|
||||||
|
|
||||||
|
|
||||||
def test_check_if_user_is_active():
|
def test_check_if_user_is_active():
|
||||||
email = random_lower_string()
|
email = random_lower_string()
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=email, password=password)
|
user_in = UserInCreate(email=email, password=password)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
is_active = crud_user.is_active(user)
|
is_active = crud.user.is_active(user)
|
||||||
assert is_active is True
|
assert is_active is True
|
||||||
|
|
||||||
|
|
||||||
@@ -48,9 +48,9 @@ def test_check_if_user_is_active_inactive():
|
|||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=email, password=password, disabled=True)
|
user_in = UserInCreate(email=email, password=password, disabled=True)
|
||||||
print(user_in)
|
print(user_in)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
print(user)
|
print(user)
|
||||||
is_active = crud_user.is_active(user)
|
is_active = crud.user.is_active(user)
|
||||||
print(is_active)
|
print(is_active)
|
||||||
assert is_active
|
assert is_active
|
||||||
|
|
||||||
@@ -59,8 +59,8 @@ def test_check_if_user_is_superuser():
|
|||||||
email = random_lower_string()
|
email = random_lower_string()
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=email, password=password, is_superuser=True)
|
user_in = UserInCreate(email=email, password=password, is_superuser=True)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
is_superuser = crud_user.is_superuser(user)
|
is_superuser = crud.user.is_superuser(user)
|
||||||
assert is_superuser is True
|
assert is_superuser is True
|
||||||
|
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ def test_check_if_user_is_superuser_normal_user():
|
|||||||
username = random_lower_string()
|
username = random_lower_string()
|
||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
user_in = UserInCreate(email=username, password=password)
|
user_in = UserInCreate(email=username, password=password)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
is_superuser = crud_user.is_superuser(user)
|
is_superuser = crud.user.is_superuser(user)
|
||||||
assert is_superuser is False
|
assert is_superuser is False
|
||||||
|
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ def test_get_user():
|
|||||||
password = random_lower_string()
|
password = random_lower_string()
|
||||||
username = random_lower_string()
|
username = random_lower_string()
|
||||||
user_in = UserInCreate(email=username, password=password, is_superuser=True)
|
user_in = UserInCreate(email=username, password=password, is_superuser=True)
|
||||||
user = crud_user.create(db_session, user_in=user_in)
|
user = crud.user.create(db_session, user_in=user_in)
|
||||||
user_2 = crud_user.get(db_session, user_id=user.id)
|
user_2 = crud.user.get(db_session, user_id=user.id)
|
||||||
assert user.email == user_2.email
|
assert user.email == user_2.email
|
||||||
assert jsonable_encoder(user) == jsonable_encoder(user_2)
|
assert jsonable_encoder(user) == jsonable_encoder(user_2)
|
||||||
|
|||||||
@@ -19,10 +19,14 @@ wait_seconds = 1
|
|||||||
after=after_log(logger, logging.WARN),
|
after=after_log(logger, logging.WARN),
|
||||||
)
|
)
|
||||||
def init():
|
def init():
|
||||||
# Try to create session to check if DB is awake
|
try:
|
||||||
db_session.execute("SELECT 1")
|
# Try to create session to check if DB is awake
|
||||||
# Wait for API to be awake, run one simple tests to authenticate
|
db_session.execute("SELECT 1")
|
||||||
test_get_access_token()
|
# Wait for API to be awake, run one simple tests to authenticate
|
||||||
|
test_get_access_token()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ def send_reset_password_email(email_to: str, email: str, token: str):
|
|||||||
|
|
||||||
def send_new_account_email(email_to: str, username: str, password: str):
|
def send_new_account_email(email_to: str, username: str, password: str):
|
||||||
project_name = config.PROJECT_NAME
|
project_name = config.PROJECT_NAME
|
||||||
subject = f"{project_name} - New acccount for user {username}"
|
subject = f"{project_name} - New account for user {username}"
|
||||||
with open(Path(config.EMAIL_TEMPLATES_DIR) / "new_account.html") as f:
|
with open(Path(config.EMAIL_TEMPLATES_DIR) / "new_account.html") as f:
|
||||||
template_str = f.read()
|
template_str = f.read()
|
||||||
link = config.SERVER_HOST
|
link = config.SERVER_HOST
|
||||||
|
|||||||
@@ -8,5 +8,4 @@ client_sentry = Client(config.SENTRY_DSN)
|
|||||||
|
|
||||||
@celery_app.task(acks_late=True)
|
@celery_app.task(acks_late=True)
|
||||||
def test_celery(word: str):
|
def test_celery(word: str):
|
||||||
print("test task")
|
|
||||||
return f"test task return {word}"
|
return f"test task return {word}"
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
#! /usr/bin/env bash
|
|
||||||
uvicorn app.main:app --host 0.0.0.0 --port 80 --debug
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.6
|
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.6
|
||||||
|
|
||||||
RUN pip install celery==4.2.1 passlib[bcrypt] tenacity requests pydantic emails "fastapi>=0.6.0" uvicorn gunicorn pyjwt python-multipart email_validator jinja2 psycopg2-binary alembic SQLAlchemy
|
RUN pip install celery==4.2.1 passlib[bcrypt] tenacity requests emails "fastapi>=0.7.1" uvicorn gunicorn pyjwt python-multipart email_validator jinja2 psycopg2-binary alembic SQLAlchemy
|
||||||
|
|
||||||
# For development, Jupyter remote kernel, Hydrogen
|
# For development, Jupyter remote kernel, Hydrogen
|
||||||
# Using inside the container:
|
# Using inside the container:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM python:3.6
|
FROM python:3.6
|
||||||
|
|
||||||
RUN pip install raven celery==4.2.1 passlib[bcrypt] tenacity requests "fastapi>=0.6.0" emails pyjwt email_validator jinja2 psycopg2-binary alembic SQLAlchemy
|
RUN pip install raven celery==4.2.1 passlib[bcrypt] tenacity requests "fastapi>=0.7.1" emails pyjwt email_validator jinja2 psycopg2-binary alembic SQLAlchemy
|
||||||
|
|
||||||
# For development, Jupyter remote kernel, Hydrogen
|
# For development, Jupyter remote kernel, Hydrogen
|
||||||
# Using inside the container:
|
# Using inside the container:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM python:3.6
|
FROM python:3.6
|
||||||
|
|
||||||
RUN pip install requests pytest tenacity passlib[bcrypt] pydantic "fastapi>=0.6.0" psycopg2-binary SQLAlchemy
|
RUN pip install requests pytest tenacity passlib[bcrypt] "fastapi>=0.7.1" psycopg2-binary SQLAlchemy
|
||||||
|
|
||||||
# For development, Jupyter remote kernel, Hydrogen
|
# For development, Jupyter remote kernel, Hydrogen
|
||||||
# Using inside the container:
|
# Using inside the container:
|
||||||
|
|||||||
212
{{cookiecutter.project_slug}}/frontend/package-lock.json
generated
212
{{cookiecutter.project_slug}}/frontend/package-lock.json
generated
@@ -858,24 +858,12 @@
|
|||||||
"integrity": "sha512-ePl4l+7dLLmCucIwgQHAgjiepY++qcI6nb8eAwGNkB6OxmTe3Z9rQU3rSpomqu42PCCnlThZbOoxsf+qylJsLA==",
|
"integrity": "sha512-ePl4l+7dLLmCucIwgQHAgjiepY++qcI6nb8eAwGNkB6OxmTe3Z9rQU3rSpomqu42PCCnlThZbOoxsf+qylJsLA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
|
||||||
"version": "10.12.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.20.tgz",
|
|
||||||
"integrity": "sha512-9spv6SklidqxevvZyOUGjZVz4QRXGu2dNaLyXIFzFYZW0AGDykzPRIUFJXTlQXyfzAucddwTcGtJNim8zqSOPA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/q": {
|
"@types/q": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz",
|
||||||
"integrity": "sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==",
|
"integrity": "sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/semver": {
|
|
||||||
"version": "5.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz",
|
|
||||||
"integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/strip-bom": {
|
"@types/strip-bom": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
|
||||||
@@ -1034,18 +1022,95 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@vue/cli-plugin-unit-jest": {
|
"@vue/cli-plugin-unit-jest": {
|
||||||
"version": "3.3.0",
|
"version": "3.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-unit-jest/-/cli-plugin-unit-jest-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-unit-jest/-/cli-plugin-unit-jest-3.5.0.tgz",
|
||||||
"integrity": "sha512-Y/WkrO95vdvjVjeNO1vZRQUAxlZ6ngdgAzvMzCeEaujbRG4b8M6W7ePSAe8C9yfoVcJtbnoHcBv2er31sPwtyQ==",
|
"integrity": "sha512-JFKiuLil1ayzTZCYk1DgoUFYb0F3nfbdVH3C7CN39EOfNgvEMvtavgS2Pb6MU+xx1f2J71bwVHQYY0HIx8zWJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/cli-shared-utils": "^3.3.0",
|
"@vue/cli-shared-utils": "^3.5.0",
|
||||||
"babel-jest": "^23.6.0",
|
"babel-jest": "^23.6.0",
|
||||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
|
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
|
||||||
"jest": "^23.6.0",
|
"jest": "^23.6.0",
|
||||||
"jest-serializer-vue": "^2.0.2",
|
"jest-serializer-vue": "^2.0.2",
|
||||||
"jest-transform-stub": "^1.0.0",
|
"jest-transform-stub": "^2.0.0",
|
||||||
"vue-jest": "^3.0.2"
|
"vue-jest": "^3.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/cli-shared-utils": {
|
||||||
|
"version": "3.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-3.5.0.tgz",
|
||||||
|
"integrity": "sha512-+EIwVMTjdfRQVEtcIhpRjNsPB2ZlopiUktlPpx6oLQdlJXwBWkFQVwuXdXHtPYxB5Kzs3VPyUfhHxnPIbNw1+Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"chalk": "^2.4.1",
|
||||||
|
"execa": "^1.0.0",
|
||||||
|
"joi": "^14.3.0",
|
||||||
|
"launch-editor": "^2.2.1",
|
||||||
|
"lru-cache": "^5.1.1",
|
||||||
|
"node-ipc": "^9.1.1",
|
||||||
|
"opn": "^5.3.0",
|
||||||
|
"ora": "^3.1.0",
|
||||||
|
"request": "^2.87.0",
|
||||||
|
"request-promise-native": "^1.0.7",
|
||||||
|
"semver": "^5.5.0",
|
||||||
|
"string.prototype.padstart": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"cli-spinners": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-yiEBmhaKPPeBj7wWm4GEdtPZK940p9pl3EANIrnJ3JnvWyrPjcFcsEq6qRUuQ7fzB0+Y82ld3p6B34xo95foWw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ora": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ora/-/ora-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-XHMZA5WieCbtg+tu0uPF8CjvwQdNzKCX6BVh3N6GFsEXH40mTk5dsw/ya1lBTUGJslcEFJFQ8cBhOgkkZXQtMA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"chalk": "^2.4.2",
|
||||||
|
"cli-cursor": "^2.1.0",
|
||||||
|
"cli-spinners": "^2.0.0",
|
||||||
|
"log-symbols": "^2.2.0",
|
||||||
|
"strip-ansi": "^5.0.0",
|
||||||
|
"wcwidth": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request-promise-core": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"lodash": "^4.17.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request-promise-native": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"request-promise-core": "1.1.2",
|
||||||
|
"stealthy-require": "^1.1.1",
|
||||||
|
"tough-cookie": "^2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-TjxrkPONqO2Z8QDCpeE2j6n0M6EwxzyDgzEeGp+FbdvaJAt//ClYi6W5my+3ROlC/hZX2KACUwDfK49Ka5eDvg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^4.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@vue/cli-service": {
|
"@vue/cli-service": {
|
||||||
@@ -1467,9 +1532,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "6.0.6",
|
"version": "6.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
|
||||||
"integrity": "sha512-5M3G/A4uBSMIlfJ+h9W125vJvPFH/zirISsW5qfxF5YzEvXJCtolLoQvM5yZft0DvMcUrPGKPOlgEu55I6iUtA==",
|
"integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3806,15 +3871,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cssom": {
|
"cssom": {
|
||||||
"version": "0.3.4",
|
"version": "0.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz",
|
||||||
"integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==",
|
"integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"cssstyle": {
|
"cssstyle": {
|
||||||
"version": "1.1.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.1.tgz",
|
||||||
"integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==",
|
"integrity": "sha512-7DYm8qe+gPx/h77QlCyFmX80+fGaE/6A/Ekl0zaszYOubvySO2saYFdQ78P29D0UsULxFKCetDGNaNRUdSF+2A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"cssom": "0.3.x"
|
"cssom": "0.3.x"
|
||||||
@@ -4266,15 +4331,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editorconfig": {
|
"editorconfig": {
|
||||||
"version": "0.15.2",
|
"version": "0.15.3",
|
||||||
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
|
||||||
"integrity": "sha512-GWjSI19PVJAM9IZRGOS+YKI8LN+/sjkSjNyvxL5ucqP9/IqtYNXBaQ/6c/hkPNYQHyOHra2KoXZI/JVpuqwmcQ==",
|
"integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "^10.11.7",
|
|
||||||
"@types/semver": "^5.5.0",
|
|
||||||
"commander": "^2.19.0",
|
"commander": "^2.19.0",
|
||||||
"lru-cache": "^4.1.3",
|
"lru-cache": "^4.1.5",
|
||||||
"semver": "^5.6.0",
|
"semver": "^5.6.0",
|
||||||
"sigmund": "^1.0.1"
|
"sigmund": "^1.0.1"
|
||||||
},
|
},
|
||||||
@@ -4433,9 +4496,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"escodegen": {
|
"escodegen": {
|
||||||
"version": "1.11.0",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz",
|
||||||
"integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==",
|
"integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"esprima": "^3.1.3",
|
"esprima": "^3.1.3",
|
||||||
@@ -5010,9 +5073,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"find-babel-config": {
|
"find-babel-config": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.0.tgz",
|
||||||
"integrity": "sha1-rMAQQ6Z0n+w0Qpvmtk9ULrtdY1U=",
|
"integrity": "sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"json5": "^0.5.1",
|
"json5": "^0.5.1",
|
||||||
@@ -5280,14 +5343,12 @@
|
|||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@@ -5302,20 +5363,17 @@
|
|||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -5432,8 +5490,7 @@
|
|||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
@@ -5445,7 +5502,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -5460,7 +5516,6 @@
|
|||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
@@ -5468,14 +5523,12 @@
|
|||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.3.5",
|
"version": "2.3.5",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
@@ -5494,7 +5547,6 @@
|
|||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
@@ -5575,8 +5627,7 @@
|
|||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@@ -5588,7 +5639,6 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@@ -5710,7 +5760,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
@@ -5951,9 +6000,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"handlebars": {
|
"handlebars": {
|
||||||
"version": "4.0.12",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz",
|
||||||
"integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==",
|
"integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"async": "^2.5.0",
|
"async": "^2.5.0",
|
||||||
@@ -8321,9 +8370,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jest-transform-stub": {
|
"jest-transform-stub": {
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-transform-stub/-/jest-transform-stub-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-transform-stub/-/jest-transform-stub-2.0.0.tgz",
|
||||||
"integrity": "sha512-7eilMk4sxi2Fiy223I+BYTS5wJQEGEBqR3D8dy5A6RWmMTnmjipw2ImGDfXzEUBieebyrnitzkJfpNOJSFklLQ==",
|
"integrity": "sha512-lspHaCRx/mBbnm3h4uMMS3R5aZzMwyNpNIJLXj4cEsV0mIUtS4IjYJLSoyjRCtnxb6RIGJ4NL2quZzfIeNhbkg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"jest-util": {
|
"jest-util": {
|
||||||
@@ -8394,9 +8443,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"js-beautify": {
|
"js-beautify": {
|
||||||
"version": "1.8.9",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.8.9.tgz",
|
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.9.0.tgz",
|
||||||
"integrity": "sha512-MwPmLywK9RSX0SPsUJjN7i+RQY9w/yC17Lbrq9ViEefpLRgqAR2BgrMN2AbifkUuhDV8tRauLhLda/9+bE0YQA==",
|
"integrity": "sha512-P0skmY4IDjfLiVrx+GLDeme8w5G0R1IGXgccVU5HP2VM3lRblH7qN2LTea5vZAxrDjpZBD0Jv+ahpjwVcbz/rw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"config-chain": "^1.1.12",
|
"config-chain": "^1.1.12",
|
||||||
@@ -9310,12 +9359,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node-notifier": {
|
"node-notifier": {
|
||||||
"version": "5.3.0",
|
"version": "5.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz",
|
||||||
"integrity": "sha512-AhENzCSGZnZJgBARsUjnQ7DnZbzyP+HxlVXuD0xqAnvL8q+OqtSX7lGg9e8nHzwXkMMXNdVeqq4E2M3EUAqX6Q==",
|
"integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"growly": "^1.3.0",
|
"growly": "^1.3.0",
|
||||||
|
"is-wsl": "^1.1.0",
|
||||||
"semver": "^5.5.0",
|
"semver": "^5.5.0",
|
||||||
"shellwords": "^0.1.1",
|
"shellwords": "^0.1.1",
|
||||||
"which": "^1.3.0"
|
"which": "^1.3.0"
|
||||||
@@ -9404,9 +9454,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"nwsapi": {
|
"nwsapi": {
|
||||||
"version": "2.0.9",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.1.tgz",
|
||||||
"integrity": "sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ==",
|
"integrity": "sha512-T5GaA1J/d34AC8mkrFD2O0DR17kwJ702ZOtJOsS8RpbsQZVOC2/xYFb1i/cw+xdM54JIlMuojjDOYct8GIWtwg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"oauth-sign": {
|
"oauth-sign": {
|
||||||
@@ -10891,9 +10941,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"realpath-native": {
|
"realpath-native": {
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz",
|
||||||
"integrity": "sha512-+S3zTvVt9yTntFrBpm7TQmQ3tzpCrnA1a/y+3cUHAc9ZR6aIjG0WNLR+Rj79QpJktY+VeW/TQtFlQ1bzsehI8g==",
|
"integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"util.promisify": "^1.0.0"
|
"util.promisify": "^1.0.0"
|
||||||
@@ -13049,9 +13099,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"vue-jest": {
|
"vue-jest": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.4.tgz",
|
||||||
"integrity": "sha512-5XIQ1xQFW0ZnWxHWM7adVA2IqbDsdw1vhgZfGFX4oWd75J38KIS3YT41PtiE7lpMLmNM6+VJ0uprT2mhHjUgkA==",
|
"integrity": "sha512-PY9Rwt4OyaVlA+KDJJ0614CbEvNOkffDI9g9moLQC/2DDoo0YrqZm7dHi13Q10uoK5Nt5WCYFdeAheOExPah0w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
|
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"@vue/cli-plugin-babel": "^3.3.0",
|
"@vue/cli-plugin-babel": "^3.3.0",
|
||||||
"@vue/cli-plugin-pwa": "^3.3.0",
|
"@vue/cli-plugin-pwa": "^3.3.0",
|
||||||
"@vue/cli-plugin-typescript": "^3.3.0",
|
"@vue/cli-plugin-typescript": "^3.3.0",
|
||||||
"@vue/cli-plugin-unit-jest": "^3.3.0",
|
"@vue/cli-plugin-unit-jest": "^3.5.0",
|
||||||
"@vue/cli-service": "^3.3.1",
|
"@vue/cli-service": "^3.3.1",
|
||||||
"@vue/test-utils": "^1.0.0-beta.28",
|
"@vue/test-utils": "^1.0.0-beta.28",
|
||||||
"babel-core": "7.0.0-bridge.0",
|
"babel-core": "7.0.0-bridge.0",
|
||||||
|
|||||||
@@ -21,8 +21,9 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { dispatchCheckLoggedIn, readIsLoggedIn, commitAddNotification } from '@/store/main/accessors';
|
|
||||||
import NotificationsManager from '@/components/NotificationsManager.vue';
|
import NotificationsManager from '@/components/NotificationsManager.vue';
|
||||||
|
import { readIsLoggedIn } from '@/store/main/getters';
|
||||||
|
import { dispatchCheckLoggedIn } from '@/store/main/actions';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
|
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
|
||||||
import { readFirstNotification, dispatchRemoveNotification, commitRemoveNotification } from '@/store/main/accessors';
|
|
||||||
import { AppNotification } from '@/store/main/state';
|
import { AppNotification } from '@/store/main/state';
|
||||||
|
import { commitRemoveNotification } from '@/store/main/mutations';
|
||||||
|
import { readFirstNotification } from '@/store/main/getters';
|
||||||
|
import { dispatchRemoveNotification } from '@/store/main/actions';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class NotificationsManager extends Vue {
|
export default class NotificationsManager extends Vue {
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import { getStoreAccessors } from 'typesafe-vuex';
|
|
||||||
import { State } from '@/store/state';
|
|
||||||
import { mutations } from '../mutations';
|
|
||||||
import { AdminState } from '../state';
|
|
||||||
|
|
||||||
const {commit} = getStoreAccessors<AdminState, State>('');
|
|
||||||
|
|
||||||
export const commitSetUser = commit(mutations.setUser);
|
|
||||||
export const commitSetUsers = commit(mutations.setUsers);
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { getStoreAccessors } from 'typesafe-vuex';
|
|
||||||
import { AdminState } from '../state';
|
|
||||||
import { State } from '@/store/state';
|
|
||||||
import { actions } from '../actions';
|
|
||||||
|
|
||||||
const {dispatch} = getStoreAccessors<AdminState, State>('');
|
|
||||||
|
|
||||||
export const dispatchCreateUser = dispatch(actions.actionCreateUser);
|
|
||||||
export const dispatchGetUsers = dispatch(actions.actionGetUsers);
|
|
||||||
export const dispatchUpdateUser = dispatch(actions.actionUpdateUser);
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export * from './commit';
|
|
||||||
export * from './dispatch';
|
|
||||||
export * from './read';
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { getStoreAccessors } from 'typesafe-vuex';
|
|
||||||
import { AdminState } from '../state';
|
|
||||||
import { State } from '@/store/state';
|
|
||||||
import { getters } from '../getters';
|
|
||||||
|
|
||||||
const { read } = getStoreAccessors<AdminState, State>('');
|
|
||||||
|
|
||||||
export const readAdminOneUser = read(getters.adminOneUser);
|
|
||||||
export const readAdminUsers = read(getters.adminUsers);
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
import { api } from '@/api';
|
import { api } from '@/api';
|
||||||
import { ActionContext } from 'vuex';
|
import { ActionContext } from 'vuex';
|
||||||
import {
|
|
||||||
commitSetUsers,
|
|
||||||
commitSetUser,
|
|
||||||
} from './accessors/commit';
|
|
||||||
import { IUserProfileCreate, IUserProfileUpdate } from '@/interfaces';
|
import { IUserProfileCreate, IUserProfileUpdate } from '@/interfaces';
|
||||||
import { State } from '../state';
|
import { State } from '../state';
|
||||||
import { AdminState } from './state';
|
import { AdminState } from './state';
|
||||||
import { dispatchCheckApiError, commitAddNotification, commitRemoveNotification } from '../main/accessors';
|
import { getStoreAccessors } from 'typesafe-vuex';
|
||||||
|
import { commitSetUsers, commitSetUser } from './mutations';
|
||||||
|
import { dispatchCheckApiError } from '../main/actions';
|
||||||
|
import { commitAddNotification, commitRemoveNotification } from '../main/mutations';
|
||||||
|
|
||||||
type MainContext = ActionContext<AdminState, State>;
|
type MainContext = ActionContext<AdminState, State>;
|
||||||
|
|
||||||
@@ -32,7 +31,7 @@ export const actions = {
|
|||||||
]))[0];
|
]))[0];
|
||||||
commitSetUser(context, response.data);
|
commitSetUser(context, response.data);
|
||||||
commitRemoveNotification(context, loadingNotification);
|
commitRemoveNotification(context, loadingNotification);
|
||||||
commitAddNotification(context, {content: 'User successfully updated', color: 'success'});
|
commitAddNotification(context, { content: 'User successfully updated', color: 'success' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await dispatchCheckApiError(context, error);
|
await dispatchCheckApiError(context, error);
|
||||||
}
|
}
|
||||||
@@ -53,3 +52,9 @@ export const actions = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { dispatch } = getStoreAccessors<AdminState, State>('');
|
||||||
|
|
||||||
|
export const dispatchCreateUser = dispatch(actions.actionCreateUser);
|
||||||
|
export const dispatchGetUsers = dispatch(actions.actionGetUsers);
|
||||||
|
export const dispatchUpdateUser = dispatch(actions.actionUpdateUser);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { AdminState } from './state';
|
import { AdminState } from './state';
|
||||||
|
import { getStoreAccessors } from 'typesafe-vuex';
|
||||||
|
import { State } from '../state';
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
adminUsers: (state: AdminState) => state.users,
|
adminUsers: (state: AdminState) => state.users,
|
||||||
@@ -9,3 +11,8 @@ export const getters = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { read } = getStoreAccessors<AdminState, State>('');
|
||||||
|
|
||||||
|
export const readAdminOneUser = read(getters.adminOneUser);
|
||||||
|
export const readAdminUsers = read(getters.adminUsers);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { IUserProfile } from '@/interfaces';
|
import { IUserProfile } from '@/interfaces';
|
||||||
import { AdminState } from './state';
|
import { AdminState } from './state';
|
||||||
|
import { getStoreAccessors } from 'typesafe-vuex';
|
||||||
|
import { State } from '../state';
|
||||||
|
|
||||||
export const mutations = {
|
export const mutations = {
|
||||||
setUsers(state: AdminState, payload: IUserProfile[]) {
|
setUsers(state: AdminState, payload: IUserProfile[]) {
|
||||||
@@ -11,3 +13,8 @@ export const mutations = {
|
|||||||
state.users = users;
|
state.users = users;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { commit } = getStoreAccessors<AdminState, State>('');
|
||||||
|
|
||||||
|
export const commitSetUser = commit(mutations.setUser);
|
||||||
|
export const commitSetUsers = commit(mutations.setUsers);
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import { getStoreAccessors } from 'typesafe-vuex';
|
|
||||||
import { MainState } from '../state';
|
|
||||||
import { State } from '@/store/state';
|
|
||||||
import { mutations } from '../mutations';
|
|
||||||
|
|
||||||
const {commit} = getStoreAccessors<MainState | any, State>('');
|
|
||||||
|
|
||||||
export const commitSetDashboardMiniDrawer = commit(mutations.setDashboardMiniDrawer);
|
|
||||||
export const commitSetDashboardShowDrawer = commit(mutations.setDashboardShowDrawer);
|
|
||||||
export const commitSetLoggedIn = commit(mutations.setLoggedIn);
|
|
||||||
export const commitSetLogInError = commit(mutations.setLogInError);
|
|
||||||
export const commitSetToken = commit(mutations.setToken);
|
|
||||||
export const commitSetUserProfile = commit(mutations.setUserProfile);
|
|
||||||
export const commitAddNotification = commit(mutations.addNotification);
|
|
||||||
export const commitRemoveNotification = commit(mutations.removeNotification);
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { getStoreAccessors } from 'typesafe-vuex';
|
|
||||||
import { MainState } from '../state';
|
|
||||||
import { State } from '@/store/state';
|
|
||||||
import { actions } from '../actions';
|
|
||||||
|
|
||||||
const {dispatch} = getStoreAccessors<MainState | any, State>('');
|
|
||||||
|
|
||||||
export const dispatchCheckApiError = dispatch(actions.actionCheckApiError);
|
|
||||||
export const dispatchCheckLoggedIn = dispatch(actions.actionCheckLoggedIn);
|
|
||||||
export const dispatchGetUserProfile = dispatch(actions.actionGetUserProfile);
|
|
||||||
export const dispatchLogIn = dispatch(actions.actionLogIn);
|
|
||||||
export const dispatchLogOut = dispatch(actions.actionLogOut);
|
|
||||||
export const dispatchUserLogOut = dispatch(actions.actionUserLogOut);
|
|
||||||
export const dispatchRemoveLogIn = dispatch(actions.actionRemoveLogIn);
|
|
||||||
export const dispatchRouteLoggedIn = dispatch(actions.actionRouteLoggedIn);
|
|
||||||
export const dispatchRouteLogOut = dispatch(actions.actionRouteLogOut);
|
|
||||||
export const dispatchUpdateUserProfile = dispatch(actions.actionUpdateUserProfile);
|
|
||||||
export const dispatchRemoveNotification = dispatch(actions.removeNotification);
|
|
||||||
export const dispatchPasswordRecovery = dispatch(actions.passwordRecovery);
|
|
||||||
export const dispatchResetPassword = dispatch(actions.resetPassword);
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export * from './commit';
|
|
||||||
export * from './dispatch';
|
|
||||||
export * from './read';
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { getStoreAccessors } from 'typesafe-vuex';
|
|
||||||
import { MainState } from '../state';
|
|
||||||
import { State } from '@/store/state';
|
|
||||||
import { getters } from '../getters';
|
|
||||||
|
|
||||||
const {read} = getStoreAccessors<MainState, State>('');
|
|
||||||
|
|
||||||
export const readDashboardMiniDrawer = read(getters.dashboardMiniDrawer);
|
|
||||||
export const readDashboardShowDrawer = read(getters.dashboardShowDrawer);
|
|
||||||
export const readHasAdminAccess = read(getters.hasAdminAccess);
|
|
||||||
export const readIsLoggedIn = read(getters.isLoggedIn);
|
|
||||||
export const readLoginError = read(getters.loginError);
|
|
||||||
export const readToken = read(getters.token);
|
|
||||||
export const readUserProfile = read(getters.userProfile);
|
|
||||||
export const readFirstNotification = read(getters.firstNotification);
|
|
||||||
@@ -1,24 +1,19 @@
|
|||||||
import { api } from '@/api';
|
import { api } from '@/api';
|
||||||
import { saveLocalToken, getLocalToken, removeLocalToken } from '@/utils';
|
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
|
import { getLocalToken, removeLocalToken, saveLocalToken } from '@/utils';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { getStoreAccessors } from 'typesafe-vuex';
|
||||||
import { ActionContext } from 'vuex';
|
import { ActionContext } from 'vuex';
|
||||||
|
import { State } from '../state';
|
||||||
import {
|
import {
|
||||||
commitSetToken,
|
commitAddNotification,
|
||||||
|
commitRemoveNotification,
|
||||||
commitSetLoggedIn,
|
commitSetLoggedIn,
|
||||||
commitSetLogInError,
|
commitSetLogInError,
|
||||||
dispatchGetUserProfile,
|
commitSetToken,
|
||||||
dispatchRouteLoggedIn,
|
|
||||||
dispatchLogOut,
|
|
||||||
commitSetUserProfile,
|
commitSetUserProfile,
|
||||||
dispatchCheckApiError,
|
} from './mutations';
|
||||||
dispatchRemoveLogIn,
|
import { AppNotification, MainState } from './state';
|
||||||
dispatchRouteLogOut,
|
|
||||||
commitRemoveNotification,
|
|
||||||
commitAddNotification,
|
|
||||||
} from './accessors';
|
|
||||||
import { AxiosError } from 'axios';
|
|
||||||
import { State } from '../state';
|
|
||||||
import { MainState, AppNotification } from './state';
|
|
||||||
|
|
||||||
type MainContext = ActionContext<MainState, State>;
|
type MainContext = ActionContext<MainState, State>;
|
||||||
|
|
||||||
@@ -160,3 +155,19 @@ export const actions = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { dispatch } = getStoreAccessors<MainState | any, State>('');
|
||||||
|
|
||||||
|
export const dispatchCheckApiError = dispatch(actions.actionCheckApiError);
|
||||||
|
export const dispatchCheckLoggedIn = dispatch(actions.actionCheckLoggedIn);
|
||||||
|
export const dispatchGetUserProfile = dispatch(actions.actionGetUserProfile);
|
||||||
|
export const dispatchLogIn = dispatch(actions.actionLogIn);
|
||||||
|
export const dispatchLogOut = dispatch(actions.actionLogOut);
|
||||||
|
export const dispatchUserLogOut = dispatch(actions.actionUserLogOut);
|
||||||
|
export const dispatchRemoveLogIn = dispatch(actions.actionRemoveLogIn);
|
||||||
|
export const dispatchRouteLoggedIn = dispatch(actions.actionRouteLoggedIn);
|
||||||
|
export const dispatchRouteLogOut = dispatch(actions.actionRouteLogOut);
|
||||||
|
export const dispatchUpdateUserProfile = dispatch(actions.actionUpdateUserProfile);
|
||||||
|
export const dispatchRemoveNotification = dispatch(actions.removeNotification);
|
||||||
|
export const dispatchPasswordRecovery = dispatch(actions.passwordRecovery);
|
||||||
|
export const dispatchResetPassword = dispatch(actions.resetPassword);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { MainState } from './state';
|
import { MainState } from './state';
|
||||||
|
import { getStoreAccessors } from 'typesafe-vuex';
|
||||||
|
import { State } from '../state';
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
hasAdminAccess: (state: MainState) => {
|
hasAdminAccess: (state: MainState) => {
|
||||||
@@ -14,3 +16,14 @@ export const getters = {
|
|||||||
isLoggedIn: (state: MainState) => state.isLoggedIn,
|
isLoggedIn: (state: MainState) => state.isLoggedIn,
|
||||||
firstNotification: (state: MainState) => state.notifications.length > 0 && state.notifications[0],
|
firstNotification: (state: MainState) => state.notifications.length > 0 && state.notifications[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const {read} = getStoreAccessors<MainState, State>('');
|
||||||
|
|
||||||
|
export const readDashboardMiniDrawer = read(getters.dashboardMiniDrawer);
|
||||||
|
export const readDashboardShowDrawer = read(getters.dashboardShowDrawer);
|
||||||
|
export const readHasAdminAccess = read(getters.hasAdminAccess);
|
||||||
|
export const readIsLoggedIn = read(getters.isLoggedIn);
|
||||||
|
export const readLoginError = read(getters.loginError);
|
||||||
|
export const readToken = read(getters.token);
|
||||||
|
export const readUserProfile = read(getters.userProfile);
|
||||||
|
export const readFirstNotification = read(getters.firstNotification);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { IUserProfile } from '@/interfaces';
|
import { IUserProfile } from '@/interfaces';
|
||||||
import { MainState, AppNotification } from './state';
|
import { MainState, AppNotification } from './state';
|
||||||
|
import { getStoreAccessors } from 'typesafe-vuex';
|
||||||
|
import { State } from '../state';
|
||||||
|
|
||||||
|
|
||||||
export const mutations = {
|
export const mutations = {
|
||||||
@@ -28,3 +30,14 @@ export const mutations = {
|
|||||||
state.notifications = state.notifications.filter((notification) => notification !== payload);
|
state.notifications = state.notifications.filter((notification) => notification !== payload);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const {commit} = getStoreAccessors<MainState | any, State>('');
|
||||||
|
|
||||||
|
export const commitSetDashboardMiniDrawer = commit(mutations.setDashboardMiniDrawer);
|
||||||
|
export const commitSetDashboardShowDrawer = commit(mutations.setDashboardShowDrawer);
|
||||||
|
export const commitSetLoggedIn = commit(mutations.setLoggedIn);
|
||||||
|
export const commitSetLogInError = commit(mutations.setLogInError);
|
||||||
|
export const commitSetToken = commit(mutations.setToken);
|
||||||
|
export const commitSetUserProfile = commit(mutations.setUserProfile);
|
||||||
|
export const commitAddNotification = commit(mutations.addNotification);
|
||||||
|
export const commitRemoveNotification = commit(mutations.removeNotification);
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { api } from '@/api';
|
import { api } from '@/api';
|
||||||
import { appName } from '@/env';
|
import { appName } from '@/env';
|
||||||
import { readLoginError, dispatchLogIn } from '@/store/main/accessors';
|
import { readLoginError } from '@/store/main/getters';
|
||||||
|
import { dispatchLogIn } from '@/store/main/actions';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class Login extends Vue {
|
export default class Login extends Vue {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { appName } from '@/env';
|
import { appName } from '@/env';
|
||||||
import { dispatchLogIn, dispatchPasswordRecovery } from '@/store/main/accessors';
|
import { dispatchPasswordRecovery } from '@/store/main/actions';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class Login extends Vue {
|
export default class Login extends Vue {
|
||||||
|
|||||||
@@ -33,13 +33,9 @@
|
|||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { Store } from 'vuex';
|
import { Store } from 'vuex';
|
||||||
import { IUserProfileUpdate } from '@/interfaces';
|
import { IUserProfileUpdate } from '@/interfaces';
|
||||||
import {
|
|
||||||
dispatchUpdateUserProfile,
|
|
||||||
readUserProfile,
|
|
||||||
dispatchResetPassword,
|
|
||||||
commitAddNotification,
|
|
||||||
} from '@/store/main/accessors';
|
|
||||||
import { appName } from '@/env';
|
import { appName } from '@/env';
|
||||||
|
import { commitAddNotification } from '@/store/main/mutations';
|
||||||
|
import { dispatchResetPassword } from '@/store/main/actions';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class UserProfileEdit extends Vue {
|
export default class UserProfileEdit extends Vue {
|
||||||
|
|||||||
@@ -6,10 +6,12 @@
|
|||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<div class="headline font-weight-light ma-5">Welcome {{greetedUser}}</div>
|
<div class="headline font-weight-light ma-5">Welcome {{greetedUser}}</div>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
<v-btn to="/main/profile/view">View Profile</v-btn>
|
<v-btn to="/main/profile/view">View Profile</v-btn>
|
||||||
<v-btn to="/main/profile/edit">Edit Profile</v-btn>
|
<v-btn to="/main/profile/edit">Edit Profile</v-btn>
|
||||||
<v-btn to="/main/profile/password">Change Password</v-btn>
|
<v-btn to="/main/profile/password">Change Password</v-btn>
|
||||||
</v-card-text>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
@@ -17,7 +19,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { Store } from 'vuex';
|
import { Store } from 'vuex';
|
||||||
import { readUserProfile } from '@/store/main/accessors';
|
import { readUserProfile } from '@/store/main/getters';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class Dashboard extends Vue {
|
export default class Dashboard extends Vue {
|
||||||
|
|||||||
@@ -121,14 +121,9 @@
|
|||||||
import { Vue, Component } from 'vue-property-decorator';
|
import { Vue, Component } from 'vue-property-decorator';
|
||||||
|
|
||||||
import { appName } from '@/env';
|
import { appName } from '@/env';
|
||||||
import {
|
import { readDashboardMiniDrawer, readDashboardShowDrawer, readHasAdminAccess } from '@/store/main/getters';
|
||||||
commitSetDashboardShowDrawer,
|
import { commitSetDashboardShowDrawer, commitSetDashboardMiniDrawer } from '@/store/main/mutations';
|
||||||
readDashboardShowDrawer,
|
import { dispatchUserLogOut } from '@/store/main/actions';
|
||||||
commitSetDashboardMiniDrawer,
|
|
||||||
readDashboardMiniDrawer,
|
|
||||||
dispatchUserLogOut,
|
|
||||||
readHasAdminAccess,
|
|
||||||
} from '@/store/main/accessors';
|
|
||||||
|
|
||||||
const routeGuardMain = async (to, from, next) => {
|
const routeGuardMain = async (to, from, next) => {
|
||||||
if (to.path === '/main') {
|
if (to.path === '/main') {
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { dispatchCheckLoggedIn, readIsLoggedIn } from '@/store/main/accessors';
|
|
||||||
import { store } from '@/store';
|
import { store } from '@/store';
|
||||||
|
import { dispatchCheckLoggedIn } from '@/store/main/actions';
|
||||||
|
import { readIsLoggedIn } from '@/store/main/getters';
|
||||||
|
|
||||||
const startRouteGuard = async (to, from, next) => {
|
const startRouteGuard = async (to, from, next) => {
|
||||||
await dispatchCheckLoggedIn(store);
|
await dispatchCheckLoggedIn(store);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { store } from '@/store';
|
import { store } from '@/store';
|
||||||
import { readHasAdminAccess } from '@/store/main/accessors';
|
import { readHasAdminAccess } from '@/store/main/getters';
|
||||||
|
|
||||||
const routeGuardAdmin = async (to, from, next) => {
|
const routeGuardAdmin = async (to, from, next) => {
|
||||||
if (!readHasAdminAccess(store)) {
|
if (!readHasAdminAccess(store)) {
|
||||||
|
|||||||
@@ -31,7 +31,8 @@
|
|||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { Store } from 'vuex';
|
import { Store } from 'vuex';
|
||||||
import { IUserProfile } from '@/interfaces';
|
import { IUserProfile } from '@/interfaces';
|
||||||
import { readAdminUsers, dispatchGetUsers } from '@/store/admin/accessors';
|
import { readAdminUsers } from '@/store/admin/getters';
|
||||||
|
import { dispatchGetUsers } from '@/store/admin/actions';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class AdminUsers extends Vue {
|
export default class AdminUsers extends Vue {
|
||||||
|
|||||||
@@ -21,14 +21,17 @@
|
|||||||
</v-text-field>
|
</v-text-field>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
<v-btn @click="submit" :disabled="!valid">
|
|
||||||
Save
|
|
||||||
</v-btn>
|
|
||||||
<v-btn @click="reset">Reset</v-btn>
|
|
||||||
<v-btn @click="cancel">Cancel</v-btn>
|
|
||||||
</v-form>
|
</v-form>
|
||||||
</template>
|
</template>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn @click="cancel">Cancel</v-btn>
|
||||||
|
<v-btn @click="reset">Reset</v-btn>
|
||||||
|
<v-btn @click="submit" :disabled="!valid">
|
||||||
|
Save
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
@@ -40,7 +43,7 @@ import {
|
|||||||
IUserProfileUpdate,
|
IUserProfileUpdate,
|
||||||
IUserProfileCreate,
|
IUserProfileCreate,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { dispatchGetUsers, dispatchCreateUser } from '@/store/admin/accessors';
|
import { dispatchGetUsers, dispatchCreateUser } from '@/store/admin/actions';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class CreateUser extends Vue {
|
export default class CreateUser extends Vue {
|
||||||
|
|||||||
@@ -8,37 +8,92 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<div class="subheading secondary--text text--lighten-2">Username</div>
|
<div class="subheading secondary--text text--lighten-2">Username</div>
|
||||||
<div class="title primary--text text--darken-2" v-if="user">{{user.email}}</div>
|
<div
|
||||||
<div class="title primary--text text--darken-2" v-else>-----</div>
|
class="title primary--text text--darken-2"
|
||||||
|
v-if="user"
|
||||||
|
>{{user.email}}</div>
|
||||||
|
<div
|
||||||
|
class="title primary--text text--darken-2"
|
||||||
|
v-else
|
||||||
|
>-----</div>
|
||||||
</div>
|
</div>
|
||||||
<v-form v-model="valid" ref="form" lazy-validation>
|
<v-form
|
||||||
<v-text-field label="Full Name" v-model="fullName" required></v-text-field>
|
v-model="valid"
|
||||||
<v-text-field label="E-mail" type="email" v-model="email" v-validate="'required|email'" data-vv-name="email" :error-messages="errors.collect('email')" required></v-text-field>
|
ref="form"
|
||||||
|
lazy-validation
|
||||||
|
>
|
||||||
|
<v-text-field
|
||||||
|
label="Full Name"
|
||||||
|
v-model="fullName"
|
||||||
|
required
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
label="E-mail"
|
||||||
|
type="email"
|
||||||
|
v-model="email"
|
||||||
|
v-validate="'required|email'"
|
||||||
|
data-vv-name="email"
|
||||||
|
:error-messages="errors.collect('email')"
|
||||||
|
required
|
||||||
|
></v-text-field>
|
||||||
<div class="subheading secondary--text text--lighten-2">User is superuser <span v-if="isSuperuser">(currently is a superuser)</span><span v-else>(currently is not a superuser)</span></div>
|
<div class="subheading secondary--text text--lighten-2">User is superuser <span v-if="isSuperuser">(currently is a superuser)</span><span v-else>(currently is not a superuser)</span></div>
|
||||||
<v-checkbox label="Is Superuser" v-model="isSuperuser"></v-checkbox>
|
<v-checkbox
|
||||||
|
label="Is Superuser"
|
||||||
|
v-model="isSuperuser"
|
||||||
|
></v-checkbox>
|
||||||
<div class="subheading secondary--text text--lighten-2">User is active <span v-if="isActive">(currently active)</span><span v-else>(currently not active)</span></div>
|
<div class="subheading secondary--text text--lighten-2">User is active <span v-if="isActive">(currently active)</span><span v-else>(currently not active)</span></div>
|
||||||
<v-checkbox label="Is Active" v-model="isActive"></v-checkbox>
|
<v-checkbox
|
||||||
|
label="Is Active"
|
||||||
|
v-model="isActive"
|
||||||
|
></v-checkbox>
|
||||||
<v-layout align-center>
|
<v-layout align-center>
|
||||||
<v-flex shrink>
|
<v-flex shrink>
|
||||||
<v-checkbox v-model="setPassword" class="mr-2"></v-checkbox>
|
<v-checkbox
|
||||||
|
v-model="setPassword"
|
||||||
|
class="mr-2"
|
||||||
|
></v-checkbox>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex>
|
<v-flex>
|
||||||
<v-text-field :disabled="!setPassword" type="password" ref="password" label="Set Password" data-vv-name="password" data-vv-delay="100" v-validate="{required: setPassword}" v-model="password1" :error-messages="errors.first('password')">
|
<v-text-field
|
||||||
|
:disabled="!setPassword"
|
||||||
|
type="password"
|
||||||
|
ref="password"
|
||||||
|
label="Set Password"
|
||||||
|
data-vv-name="password"
|
||||||
|
data-vv-delay="100"
|
||||||
|
v-validate="{required: setPassword}"
|
||||||
|
v-model="password1"
|
||||||
|
:error-messages="errors.first('password')"
|
||||||
|
>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-text-field v-show="setPassword" type="password" label="Confirm Password" data-vv-name="password_confirmation" data-vv-delay="100" data-vv-as="password" v-validate="{required: setPassword, confirmed: 'password'}" v-model="password2" :error-messages="errors.first('password_confirmation')">
|
<v-text-field
|
||||||
|
v-show="setPassword"
|
||||||
|
type="password"
|
||||||
|
label="Confirm Password"
|
||||||
|
data-vv-name="password_confirmation"
|
||||||
|
data-vv-delay="100"
|
||||||
|
data-vv-as="password"
|
||||||
|
v-validate="{required: setPassword, confirmed: 'password'}"
|
||||||
|
v-model="password2"
|
||||||
|
:error-messages="errors.first('password_confirmation')"
|
||||||
|
>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
|
||||||
</v-layout>
|
</v-layout>
|
||||||
|
|
||||||
<v-btn @click="submit" :disabled="!valid">
|
|
||||||
Save
|
|
||||||
</v-btn>
|
|
||||||
<v-btn @click="reset">Reset</v-btn>
|
|
||||||
<v-btn @click="cancel">Cancel</v-btn>
|
|
||||||
</v-form>
|
</v-form>
|
||||||
</template>
|
</template>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn @click="cancel">Cancel</v-btn>
|
||||||
|
<v-btn @click="reset">Reset</v-btn>
|
||||||
|
<v-btn
|
||||||
|
@click="submit"
|
||||||
|
:disabled="!valid"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
@@ -46,11 +101,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { IUserProfile, IUserProfileUpdate } from '@/interfaces';
|
import { IUserProfile, IUserProfileUpdate } from '@/interfaces';
|
||||||
import {
|
import { dispatchGetUsers, dispatchUpdateUser } from '@/store/admin/actions';
|
||||||
dispatchGetUsers,
|
import { readAdminOneUser } from '@/store/admin/getters';
|
||||||
dispatchUpdateUser,
|
|
||||||
readAdminOneUser,
|
|
||||||
} from '@/store/admin/accessors';
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class EditUser extends Vue {
|
export default class EditUser extends Vue {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { Store } from 'vuex';
|
import { Store } from 'vuex';
|
||||||
import { readUserProfile } from '@/store/main/accessors';
|
import { readUserProfile } from '@/store/main/getters';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class UserProfile extends Vue {
|
export default class UserProfile extends Vue {
|
||||||
|
|||||||
@@ -6,17 +6,39 @@
|
|||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<template>
|
<template>
|
||||||
<v-form v-model="valid" ref="form" lazy-validation>
|
<v-form
|
||||||
<v-text-field label="Full Name" v-model="fullName" required></v-text-field>
|
v-model="valid"
|
||||||
<v-text-field label="E-mail" type="email" v-model="email" v-validate="'required|email'" data-vv-name="email" :error-messages="errors.collect('email')" required></v-text-field>
|
ref="form"
|
||||||
<v-btn @click="submit" :disabled="!valid">
|
lazy-validation
|
||||||
Save
|
>
|
||||||
</v-btn>
|
<v-text-field
|
||||||
<v-btn @click="reset">Reset</v-btn>
|
label="Full Name"
|
||||||
<v-btn @click="cancel">Cancel</v-btn>
|
v-model="fullName"
|
||||||
|
required
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
label="E-mail"
|
||||||
|
type="email"
|
||||||
|
v-model="email"
|
||||||
|
v-validate="'required|email'"
|
||||||
|
data-vv-name="email"
|
||||||
|
:error-messages="errors.collect('email')"
|
||||||
|
required
|
||||||
|
></v-text-field>
|
||||||
</v-form>
|
</v-form>
|
||||||
</template>
|
</template>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn @click="cancel">Cancel</v-btn>
|
||||||
|
<v-btn @click="reset">Reset</v-btn>
|
||||||
|
<v-btn
|
||||||
|
@click="submit"
|
||||||
|
:disabled="!valid"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
@@ -25,7 +47,8 @@
|
|||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { Store } from 'vuex';
|
import { Store } from 'vuex';
|
||||||
import { IUserProfileUpdate } from '@/interfaces';
|
import { IUserProfileUpdate } from '@/interfaces';
|
||||||
import { dispatchUpdateUserProfile, readUserProfile } from '@/store/main/accessors';
|
import { readUserProfile } from '@/store/main/getters';
|
||||||
|
import { dispatchUpdateUserProfile } from '@/store/main/actions';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class UserProfileEdit extends Vue {
|
export default class UserProfileEdit extends Vue {
|
||||||
|
|||||||
@@ -34,12 +34,15 @@
|
|||||||
v-model="password2"
|
v-model="password2"
|
||||||
:error-messages="errors.first('password_confirmation')">
|
:error-messages="errors.first('password_confirmation')">
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-btn @click="cancel">Cancel</v-btn>
|
|
||||||
<v-btn @click="reset">Reset</v-btn>
|
|
||||||
<v-btn @click="submit" :disabled="!valid">Save</v-btn>
|
|
||||||
</v-form>
|
</v-form>
|
||||||
</template>
|
</template>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn @click="cancel">Cancel</v-btn>
|
||||||
|
<v-btn @click="reset">Reset</v-btn>
|
||||||
|
<v-btn @click="submit" :disabled="!valid">Save</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
@@ -48,7 +51,8 @@
|
|||||||
import { Component, Vue } from 'vue-property-decorator';
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
import { Store } from 'vuex';
|
import { Store } from 'vuex';
|
||||||
import { IUserProfileUpdate } from '@/interfaces';
|
import { IUserProfileUpdate } from '@/interfaces';
|
||||||
import { dispatchUpdateUserProfile, readUserProfile } from '@/store/main/accessors';
|
import { readUserProfile } from '@/store/main/getters';
|
||||||
|
import { dispatchUpdateUserProfile } from '@/store/main/actions';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class UserProfileEdit extends Vue {
|
export default class UserProfileEdit extends Vue {
|
||||||
|
|||||||
Reference in New Issue
Block a user