Settings working now. Everything working now.

Time for docker and beta test
This commit is contained in:
Amritanshu Agrawal 2020-05-30 23:39:02 +05:30
parent 95e92fc2bd
commit 013fce2e96
16 changed files with 73 additions and 214 deletions

15
.env
View File

@ -1,13 +1,18 @@
HOST=0.0.0.0
PORT=9998
LOG_LEVEL=info
LOG_LEVEL=WARN
DEBUG=true
DB_URL=postgresql://postgres:123456@localhost:5432/hops
SQLALCHEMY_DATABASE_URI=postgresql://postgres:123456@localhost:5432/hops
MODULE_NAME=brewman.main
SERVER_NAME=localhost
SERVER_HOST=localhost
PROJECT_NAME=bifrost
PROJECT_NAME=brewman
POSTGRES_SERVER=localhost
POSTGRES_USER=postgres
POSTGRES_PASSWORD=123456
POSTGRES_DB=hops
SECRET_KEY=8546a61262dab7c05ccf2e26abe30bc10966904df6dfd29259ea85dd0844a8e7
ALGORITHM=HS256
JWT_TOKEN_EXPIRE_MINUTES=30
ALEMBIC_LOG_LEVEL=INFO
ALEMBIC_SQLALCHEMY_LOG_LEVEL=WARN

View File

@ -1,85 +1,3 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; this defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = postgresql://postgres:123456@localhost:5432/hops
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks=black
# black.type=console_scripts
# black.entrypoint=black
# black.options=-l 79
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View File

@ -1,22 +1,20 @@
import os
from logging.config import fileConfig
import logging
from sqlalchemy import engine_from_config
from sqlalchemy import create_engine
from sqlalchemy import pool
from alembic import context
from brewman.core.config import settings
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(settings.ALEMBIC_LOG_LEVEL)
logging.getLogger('alembic').setLevel(settings.ALEMBIC_LOG_LEVEL)
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
from brewman.models.auth import User # noqa
target_metadata = User.metadata
print(target_metadata)
# other values from the config, defined by the needs of env.py,
# can be acquired:
@ -24,14 +22,6 @@ print(target_metadata)
# ... etc.
def get_url():
user = os.getenv("POSTGRES_USER", "postgres")
password = os.getenv("POSTGRES_PASSWORD", "123456")
server = os.getenv("POSTGRES_SERVER", "localhost")
db = os.getenv("POSTGRES_DB", "hops")
return f"postgresql://{user}:{password}@{server}/{db}"
def run_migrations_offline():
"""Run migrations in 'offline' mode.
@ -44,7 +34,7 @@ def run_migrations_offline():
script output.
"""
url = get_url()
url = settings.SQLALCHEMY_DATABASE_URI
context.configure(
url=url,
target_metadata=target_metadata,
@ -64,13 +54,7 @@ def run_migrations_online():
and associate a connection with the context.
"""
configuration = config.get_section(config.config_ini_section)
configuration["sqlalchemy.url"] = get_url()
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
connectable = create_engine(settings.SQLALCHEMY_DATABASE_URI, poolclass=pool.NullPool, )
with connectable.connect() as connection:
context.configure(

View File

@ -1,24 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@ -1,12 +0,0 @@
from environs import Env
env = Env()
env.read_env() # read .env file, if it exists
class Settings:
debug: bool = env("DEBUG")
host: str = env("HOST")
port: int = env.int("PORT")
db_url: str = env("DB_URL")
log_level: str = env("LOG_LEVEL")

View File

@ -1,36 +1,24 @@
from dotenv import load_dotenv
import secrets
from typing import Any, Dict, List, Optional, Union
from typing import Any, Dict, Optional
from pydantic import AnyHttpUrl, BaseSettings, PostgresDsn, validator
from pydantic import BaseSettings, PostgresDsn, validator
class Settings(BaseSettings):
API_V1_STR: str = "/api/v1"
# openssl rand -hex 32
SECRET_KEY: str = secrets.token_urlsafe(32)
# 60 minutes * 24 hours * 8 days = 8 days
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
SERVER_NAME: str = "localhost"
SERVER_HOST: AnyHttpUrl = "http://localhost:9998"
# BACKEND_CORS_ORIGINS is a JSON-formatted list of origins
# e.g: '["http://localhost", "http://localhost:4200", "http://localhost:3000", \
# "http://localhost:8080", "http://local.dockertoolbox.tiangolo.com"]'
BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = []
@validator("BACKEND_CORS_ORIGINS", pre=True)
def assemble_cors_origins(cls, v: Union[str, List[str]]) -> Union[List[str], str]:
if isinstance(v, str) and not v.startswith("["):
return [i.strip() for i in v.split(",")]
elif isinstance(v, (list, str)):
return v
raise ValueError(v)
PROJECT_NAME: str = "bifrost"
POSTGRES_SERVER: str = "localhost"
ALGORITHM: str = "HS256"
JWT_TOKEN_EXPIRE_MINUTES: int = 30
HOST: str = "0.0.0.0"
PORT: int = 80
DEBUG: bool = False
LOG_LEVEL: str = "NOTSET"
POSTGRES_SERVER: str = ""
POSTGRES_USER: str = "postgres"
POSTGRES_PASSWORD: str = "123456"
POSTGRES_DB: str = "hops"
SQLALCHEMY_DATABASE_URI: Optional[PostgresDsn] = None
POSTGRES_PASSWORD: str = ""
POSTGRES_DB: str = ""
SQLALCHEMY_DATABASE_URI: Optional[str] = None
@validator("SQLALCHEMY_DATABASE_URI", pre=True)
def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any:
@ -44,9 +32,13 @@ class Settings(BaseSettings):
path=f"/{values.get('POSTGRES_DB') or ''}",
)
ALEMBIC_LOG_LEVEL: str = "INFO"
ALEMBIC_SQLALCHEMY_LOG_LEVEL: str = "WARN"
class Config:
case_sensitive = True
env_file = '.env'
load_dotenv()
settings = Settings()

View File

@ -1,6 +1,6 @@
import uuid
from datetime import datetime, timedelta
from typing import List, Union, Optional
from typing import List, Optional
from jwt import PyJWTError
from fastapi import Depends, HTTPException, status, Security
@ -10,19 +10,14 @@ from sqlalchemy.orm import Session
from jose import jwt
from jose.exceptions import ExpiredSignatureError
from brewman.core.config import settings
from brewman.models.auth import User as UserModel, Client
from ..db.session import SessionLocal
# to get a string like this run:
# openssl rand -hex 32
from ..schemas.auth import UserToken
SECRET_KEY = "8546a61262dab7c05ccf2e26abe30bc10966904df6dfd29259ea85dd0844a8e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token", scopes={})
@ -58,7 +53,7 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None):
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt
@ -116,7 +111,7 @@ async def get_current_user(
headers={"WWW-Authenticate": authenticate_value},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception

View File

@ -1,7 +1,10 @@
import logging
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from brewman.core.config import settings
logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(settings.LOG_LEVEL)
engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

View File

@ -53,7 +53,7 @@ from .routers.reports import (
)
from .db.base_class import Base
from .config import Settings
from .core.config import settings
from .db.session import engine
Base.metadata.create_all(bind=engine)
@ -149,4 +149,4 @@ app.include_router(rebase.router, prefix="/api/rebase", tags=["management"])
def init():
uvicorn.run(app, host=Settings.host, port=Settings.port)
uvicorn.run(app, host=settings.HOST, port=settings.PORT)

View File

@ -7,9 +7,9 @@ from sqlalchemy.orm import Session
from ..core.security import (
Token,
authenticate_user,
ACCESS_TOKEN_EXPIRE_MINUTES,
create_access_token, get_current_active_user, client_allowed,
)
from brewman.core.config import settings
from ..db.session import SessionLocal
from ..schemas.auth import UserToken
@ -52,7 +52,7 @@ async def login_for_access_token(
)
not_allowed_response.set_cookie(key="client_id", value=str(c_id), max_age=10 * 365 * 24 * 60 * 60)
return not_allowed_response
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token_expires = timedelta(minutes=settings.JWT_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={
"sub": user.name,
@ -78,7 +78,7 @@ async def login_for_access_token(
async def refresh_token(
user: UserToken = Security(get_current_active_user)
):
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token_expires = timedelta(minutes=settings.JWT_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={
"sub": user.name,

View File

@ -4,7 +4,6 @@ import { MatSort } from '@angular/material/sort';
import { map, tap } from 'rxjs/operators';
import { merge, Observable, of as observableOf } from 'rxjs';
import { Account } from '../../core/account';
import {Employee} from "../../employee/employee";
export class AccountListDataSource extends DataSource<Account> {

View File

@ -1,14 +1,14 @@
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {BehaviorSubject, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import {User} from '../core/user';
import { User } from '../core/user';
import { environment } from '../../environments/environment';
const loginUrl = '/token';
const refreshUrl = '/refresh';
const JWT_USER = 'JWT_USER';
const ACCESS_TOKEN_REFRESH_MINUTES = 10; // refresh token 10 minutes before expiry
@Injectable({providedIn: 'root'})
export class AuthService {
@ -60,10 +60,7 @@ export class AuthService {
}
needsRefreshing(): boolean {
// We use this line to debug token refreshing
// console.log("\n", Date.now(), ": Date.now()\n", this.user.exp * 1000, ": user.exp\n",(this.user.exp - (ACCESS_TOKEN_REFRESH_MINUTES * 60)) * 1000, ": comp");
return Date.now() > (this.user.exp - (ACCESS_TOKEN_REFRESH_MINUTES * 60)) * 1000;
return Date.now() > (this.user.exp - (environment.ACCESS_TOKEN_REFRESH_MINUTES * 60)) * 1000;
}
expired(): boolean {
@ -76,10 +73,6 @@ export class AuthService {
this.currentUserSubject.next(null);
}
getJwtToken() {
return JSON.parse(localStorage.getItem(JWT_USER)).access_token;
}
refreshToken() {
return this.http.post<any>(refreshUrl, {})
.pipe(map(u => u.access_token))

View File

@ -1,13 +1,12 @@
import {DataSource} from '@angular/cdk/collections';
import { DataSource } from '@angular/cdk/collections';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import {map, tap} from 'rxjs/operators';
import {merge, Observable, of as observableOf} from 'rxjs';
import {Product} from '../../core/product';
import { map, tap } from 'rxjs/operators';
import { merge, Observable, of as observableOf } from 'rxjs';
import { Product } from '../../core/product';
export class ProductListDataSource extends DataSource<Product> {
private dataObservable: Observable<Product[]>;
private filterValue: string;
constructor(private paginator: MatPaginator, private sort: MatSort, private filter: Observable<string>, public data: Product[]) {
@ -18,9 +17,8 @@ export class ProductListDataSource extends DataSource<Product> {
}
connect(): Observable<Product[]> {
this.dataObservable = observableOf(this.data);
const dataMutations = [
this.dataObservable,
observableOf(this.data),
this.filter,
this.paginator.page,
this.sort.sortChange
@ -28,9 +26,13 @@ export class ProductListDataSource extends DataSource<Product> {
return merge(...dataMutations).pipe(
map((x: any) => {
return this.getPagedData(this.getSortedData(this.getFilteredData([...this.data])));
return this.getFilteredData([...this.data]);
}),
tap((x: Product[]) => this.paginator.length = x.length)
).pipe(
map((x: any) => {
return this.getPagedData(this.getSortedData(x));
})
);
}
@ -66,6 +68,8 @@ export class ProductListDataSource extends DataSource<Product> {
switch (this.sort.active) {
case 'name':
return compare(a.name, b.name, isAsc);
case 'productGroup':
return compare(a.productGroup, b.productGroup, isAsc);
case 'id':
return compare(+a.id, +b.id, isAsc);
default:

View File

@ -1,3 +1,4 @@
export const environment = {
production: true
production: true,
ACCESS_TOKEN_REFRESH_MINUTES: 10 // refresh token 10 minutes before expiry
};

View File

@ -3,7 +3,8 @@
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
production: false,
ACCESS_TOKEN_REFRESH_MINUTES: 10 // refresh token 10 minutes before expiry
};
/*

View File

@ -2,7 +2,6 @@ setuptools
wheel
uvicorn
fastapi
environs
python-jose[cryptography]
passlib[bcrypt]
psycopg2-binary
@ -11,5 +10,6 @@ python-multipart
pyjwt
alembic
itsdangerous
pydantic
python-dotenv
pydantic[dotenv]
starlette