Updated with active subscription

This commit is contained in:
Saurab-Shrestha 2024-02-25 12:47:35 +05:45
parent 808a0fa6fc
commit 8271fedb78
15 changed files with 121 additions and 240 deletions

2
.env
View File

@ -4,7 +4,7 @@ ENVIRONMENT=dev
DB_HOST=localhost
DB_USER=postgres
DB_PORT=5432
DB_PASSWORD=quick
DB_PASSWORD=admin
DB_NAME=QuickGpt
SUPER_ADMIN_EMAIL=superadmin@email.com

View File

@ -1,52 +0,0 @@
"""Updated audit log
Revision ID: 62c1a29320fc
Revises: b526c0676007
Create Date: 2024-02-24 17:27:33.407895
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = '62c1a29320fc'
down_revision: Union[str, None] = 'b526c0676007'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('audit', 'timestamp',
existing_type=postgresql.TIMESTAMP(),
nullable=False)
op.alter_column('audit', 'model',
existing_type=sa.VARCHAR(),
nullable=False)
op.alter_column('audit', 'action',
existing_type=sa.VARCHAR(),
nullable=False)
op.drop_column('audit', 'url')
op.drop_column('audit', 'model_id')
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
op.add_column('audit', sa.Column('model_id', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('audit', sa.Column('url', sa.VARCHAR(), autoincrement=False, nullable=True))
op.alter_column('audit', 'action',
existing_type=sa.VARCHAR(),
nullable=True)
op.alter_column('audit', 'model',
existing_type=sa.VARCHAR(),
nullable=True)
op.alter_column('audit', 'timestamp',
existing_type=postgresql.TIMESTAMP(),
nullable=True)
# ### end Alembic commands ###

View File

@ -1,18 +1,18 @@
"""Audit log
"""Create audit model
Revision ID: eb0a7f182c75
Revision ID: 9aa759c05b19
Revises:
Create Date: 2024-02-24 11:37:43.713632
Create Date: 2024-02-25 10:03:28.092131
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = 'eb0a7f182c75'
revision: str = '9aa759c05b19'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
@ -22,19 +22,23 @@ def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('audit',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('url', sa.String(), nullable=True),
sa.Column('headers', sa.ARRAY(sa.String()), nullable=True),
sa.Column('method', sa.String(), nullable=True),
sa.Column('response', sa.String(), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('model', sa.String(), nullable=False),
sa.Column('action', sa.String(), nullable=False),
sa.Column('details', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_audit_id'), 'audit', ['id'], unique=False)
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
op.add_column('users', sa.Column('password_created', sa.DateTime(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'password_created')
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
op.drop_index(op.f('ix_audit_id'), table_name='audit')
op.drop_table('audit')

View File

@ -1,50 +0,0 @@
"""Audit log update
Revision ID: b526c0676007
Revises: eb0a7f182c75
Create Date: 2024-02-24 12:00:11.632187
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = 'b526c0676007'
down_revision: Union[str, None] = 'eb0a7f182c75'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('audit', sa.Column('timestamp', sa.DateTime(), nullable=True))
op.add_column('audit', sa.Column('user_id', sa.Integer(), nullable=True))
op.add_column('audit', sa.Column('model', sa.String(), nullable=True))
op.add_column('audit', sa.Column('model_id', sa.Integer(), nullable=True))
op.add_column('audit', sa.Column('action', sa.String(), nullable=True))
op.add_column('audit', sa.Column('details', postgresql.JSONB(astext_type=sa.Text()), nullable=True))
op.create_foreign_key(None, 'audit', 'users', ['user_id'], ['id'])
op.drop_column('audit', 'headers')
op.drop_column('audit', 'response')
op.drop_column('audit', 'method')
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
op.add_column('audit', sa.Column('method', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('audit', sa.Column('response', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('audit', sa.Column('headers', postgresql.ARRAY(sa.VARCHAR()), autoincrement=False, nullable=True))
op.drop_constraint(None, 'audit', type_='foreignkey')
op.drop_column('audit', 'details')
op.drop_column('audit', 'action')
op.drop_column('audit', 'model_id')
op.drop_column('audit', 'model')
op.drop_column('audit', 'user_id')
op.drop_column('audit', 'timestamp')
# ### end Alembic commands ###

View File

@ -1,32 +0,0 @@
"""added password expiry
Revision ID: b7b23e5d5214
Revises: 62c1a29320fc
Create Date: 2024-02-24 19:47:49.069873
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'b7b23e5d5214'
down_revision: Union[str, None] = '62c1a29320fc'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
op.add_column('users', sa.Column('password_created', sa.DateTime(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'password_created')
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
# ### end Alembic commands ###

View File

@ -103,6 +103,7 @@ def chat_completion(
use_context=body.use_context,
context_filter=body.context_filter,
)
return to_openai_response(
completion.response, completion.sources if body.include_sources else None
)

View File

@ -106,6 +106,7 @@ def prompt_completion(
db: Session = Depends(deps.get_db),
current_user: models.User = Security(
deps.get_current_user,
deps.get_active_subscription,
),
) -> OpenAICompletion | StreamingResponse:
try:

View File

@ -160,10 +160,17 @@ def delete_file(
print("Unable to delete file from the static directory")
document = crud.documents.get_by_filename(db,file_name=filename)
if document:
log_audit(model='Document', action='delete',
details={"status": "SUCCESS", "message": f"{filename}' successfully deleted."}, user_id=current_user.id)
log_audit(
model='Document',
action='delete',
details={
"detail": f"{filename}' deleted successfully.",
'user': current_user.fullname,
},
user_id=current_user.id
)
crud.documents.remove(db=db, id=document.id)
return {"status": "SUCCESS", "message": f"{filename}' successfully deleted."}
return {"status": "SUCCESS", "message": f"{filename}' deleted successfully."}
except Exception as e:
print(traceback.print_exc())
logger.error(
@ -200,14 +207,14 @@ def ingest_file(
detail="No file name provided",
)
try:
docs_in = schemas.DocumentCreate(filename=file.filename, uploaded_by=current_user.id, department_id=current_user.department_id)
crud.documents.create(db=db, obj_in=docs_in)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Unable to upload file.",
)
# try:
docs_in = schemas.DocumentCreate(filename=file.filename, uploaded_by=current_user.id, department_id=current_user.department_id)
crud.documents.create(db=db, obj_in=docs_in)
# except Exception as e:
# raise HTTPException(
# status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# detail="Unable to upload file.",
# )
upload_path = Path(f"{UPLOAD_DIR}/{file.filename}")
with open(upload_path, "wb") as f:
@ -218,11 +225,13 @@ def ingest_file(
logger.info(f"{file.filename} is uploaded by the {current_user.fullname}.")
response = IngestResponse(
object="list", model="private-gpt", data=ingested_documents)
log_audit(model='Document', action='create',
details={
'filename': file.filename,
'filename': f"{file.filename} uploaded successfully",
'user': current_user.fullname,
}, user_id=current_user.id)
return response
except HTTPException:
print(traceback.print_exc())
@ -274,8 +283,16 @@ async def common_ingest_logic(
f.write(file.read())
file.seek(0)
ingested_documents = service.ingest_bin_data(file_name, file)
log_audit(model='Document', action='create',
details={'status': "SUCCESS", 'message': f"{file_name} uploaded successfully."}, user_id=current_user.id)
log_audit(
model='Document',
action='create',
details={
'detail': f"{file_name} uploaded successfully",
'user': f"{current_user.fullname}"
},
user_id=current_user.id
)
logger.info(
f"{file_name} is uploaded by the {current_user.fullname}.")

View File

@ -117,17 +117,16 @@ def get_company_name(company_id: int, db: Session = Depends(get_db)) -> str:
def get_active_subscription(
current_user: models.User = Depends(get_current_user),
db: Session = Depends(get_db),
):
company_id = current_user.user_role.company_id
company_id = 1
if company_id:
company = crud.company.get(db, company_id)
if company and company.subscriptions:
active_subscription = next((sub for sub in company.subscriptions if sub.is_active), None)
if active_subscription:
return company
print("Has active Subscription")
return active_subscription
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Access Forbidden - No Active Subscription",

View File

@ -1,3 +1,4 @@
from private_gpt.users.api import deps
from private_gpt.users.api.v1.routers import auth, roles, user_roles, users, subscriptions, companies, departments, documents, audits
from fastapi import APIRouter

View File

@ -111,6 +111,7 @@ def login_access_token(
log_audit: models.Audit = Depends(deps.get_audit_logger),
db: Session = Depends(deps.get_db),
form_data: OAuth2PasswordRequestForm = Depends(),
active_subscription: models.Subscription = Depends(deps.get_active_subscription)
) -> Any:
"""
OAuth2 compatible token login, get an access token for future requests
@ -208,7 +209,7 @@ def register(
# password: str = Body(...),
company_id: int = Body(None, title="Company ID",
description="Company ID for the user (if applicable)"),
department_id: str = Body(None, title="Department ID",
department_id: int = Body(None, title="Department ID",
description="Department name for the user (if applicable)"),
role_name: str = Body(None, title="Role Name",
description="User role name (if applicable)"),

View File

@ -92,7 +92,7 @@ def create_department(
department1 = jsonable_encoder(department)
details = {
'user_id': current_user.id,
'detail': 'Department created successfully',
'department_id': department.id,
'department_name': department.name
}
@ -131,16 +131,6 @@ def read_department(
department = crud.department.get_by_id(db, id=department_id)
if department is None:
raise HTTPException(status_code=404, detail="Department not found")
details = {
'status': 200,
'user_id': current_user.id,
'department_id': department.id,
'department_name': department.name
}
log_audit_department(db, current_user, 'read', details)
return department
except Exception as e:
print(traceback.format_exc())
@ -174,8 +164,7 @@ def update_department(
updated_department = jsonable_encoder(updated_department)
details = {
'status': '200',
'user_id': current_user.id,
'detail': 'Department updated successfully!',
'department_id': department.id,
'old_department_name': old_name,
'new_department_name': department.name,
@ -218,18 +207,16 @@ def delete_department(
raise HTTPException(status_code=404, detail="Department not found")
details = {
'status': 200,
'user_id': current_user.id,
'detail': "Department deleted successfully!",
'department_id': department.id,
'department_name': department.name
}
log_audit_department(db, current_user, 'delete', details)
deleted_department = crud.department.remove(db=db, id=department_id)
log_audit_department(db, current_user, 'delete', details)
deleted_department = jsonable_encoder(deleted_department)
return JSONResponse(
status_code=status.HTTP_200_OK,
content={

View File

@ -127,6 +127,7 @@ def update_username(
Update own username.
"""
user_in = schemas.UserUpdate(fullname=update_in.fullname, email=current_user.email, company_id=current_user.company_id)
old_fullname = current_user.fullname
user = crud.user.update(db, db_obj=current_user, obj_in=user_in)
user_data = schemas.UserBaseSchema(
email=user.email,
@ -135,11 +136,8 @@ def update_username(
department_id=user.department_id,
)
details = {
'user_id': user.id,
'email': user.email,
'fullname': user.fullname,
'company_id': user.company_id,
'department_id': user.department_id,
'old_fullname': old_fullname,
'new_fullname': user.fullname,
}
log_audit_user(db, current_user, 'update_username', details)
return JSONResponse(
@ -200,11 +198,8 @@ def change_password(
)
details = {
'detail': 'Password changed successfully!',
'user_id': current_user.id,
'email': current_user.email,
'fullname': current_user.fullname,
'company_id': current_user.company_id,
'department_id': current_user.department_id,
}
log_audit_user(db, current_user, 'change_password', details)
@ -346,11 +341,9 @@ def delete_user(
user = crud.user.get(db, id=user_id)
details = {
'admin_id': current_user.id,
'deleted_user_id': user.id,
'detail': "user deleted successfully!",
'email': user.email,
'fullname': user.fullname,
'company_id': user.company_id,
'department_id': user.department_id,
}
@ -378,53 +371,66 @@ def admin_update_user(
"""
Update the user by the Admin/Super_ADMIN
"""
existing_user = crud.user.get(db, id=user_update.id)
try:
if existing_user is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"User not found with id: {user_update.id}",
)
if existing_user.fullname == user_update.fullname:
pass
else:
fullname = crud.user.get_by_name(db, name=user_update.fullname)
if fullname:
existing_user = crud.user.get_by_id(db, id=user_update.id)
if existing_user is None:
raise HTTPException(
status_code=409,
detail="The user with this username already exists!",
status_code=status.HTTP_404_NOT_FOUND,
detail=f"User not found with id: {user_update.id}",
)
old_detail = {
'fullnam': existing_user.fullname,
'role': existing_user.user_role.role.name,
'department': existing_user.department_id
}
if existing_user.fullname == user_update.fullname:
pass
else:
fullname = crud.user.get_by_name(db, name=user_update.fullname)
if fullname:
raise HTTPException(
status_code=409,
detail="The user with this username already exists!",
)
role = crud.role.get_by_name(db, name=user_update.role)
if role.id == 1:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Cannot create SUPER ADMIN!",
)
role = crud.role.get_by_name(db, name=user_update.role)
if role.id == 1:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Cannot create SUPER ADMIN!",
user_role = crud.user_role.get_by_user_id(db, user_id=existing_user.id)
role_in = schemas.UserRoleUpdate(
user_id=existing_user.id,
role_id=role.id,
)
role = crud.user_role.update(db, db_obj=user_role, obj_in=role_in)
print(f"THe new user name : {user_update.fullname} Department: {user_update.department_id}")
user_update_in = schemas.UserAdmin(fullname=user_update.fullname, department_id=user_update.department_id)
user_role = crud.user_role.get_by_user_id(db, user_id=existing_user.id)
role_in = schemas.UserRoleUpdate(
user_id=existing_user.id,
role_id=role.id,
)
role = crud.user_role.update(db, db_obj=user_role, obj_in=role_in)
new_detail = {
'email': existing_user.email,
'fullname': existing_user.fullname,
'department_id': existing_user.department_id,
}
details = {
'old detail': old_detail,
'new detail': new_detail,
}
log_audit_user(db, current_user, 'admin_update', details)
user = crud.user.get_by_id(db, id=existing_user.id)
crud.user.update(db, db_obj=user, obj_in=user_update_in)
user_in = schemas.UserAdmin(fullname=user_update.fullname, department_id=user_update.department_id)
details = {
'admin_id': current_user.id,
'user_id': existing_user.id,
'email': existing_user.email,
'fullname': existing_user.fullname,
'company_id': existing_user.company_id,
'department_id': existing_user.department_id,
}
log_audit_user(db, current_user, 'admin_update', details)
crud.user.update(db, db_obj=existing_user, obj_in=user_in)
return JSONResponse(
status_code=status.HTTP_200_OK,
content={"message": "User updated successfully",
}
)
return JSONResponse(
status_code=status.HTTP_200_OK,
content={"message": "User updated successfully"}
)
except Exception as e:
print(traceback.print_exc())
return HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail='Unable to update user'
)

View File

@ -8,7 +8,7 @@ SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://{username}:{password}@{host}:{p
port='5432',
db_name='QuickGpt',
username='postgres',
password="quick",
password="admin",
)
class Settings(BaseSettings):

View File

@ -17,11 +17,9 @@ class CRUDDepartments(CRUDBase[Department, DepartmentCreate, DepartmentUpdate]):
) -> List[Department]:
return (
db.query(self.model)
.filter(Department.department_id == department_id)
.filter(Department.id == department_id)
.offset(skip)
.limit(limit)
.all()
)
department = CRUDDepartments(Department)