mirror of
https://github.com/imartinez/privateGPT.git
synced 2025-07-01 01:32:01 +00:00
Added audit logs
This commit is contained in:
parent
59d9413898
commit
493963908d
2
.env
2
.env
@ -4,7 +4,7 @@ ENVIRONMENT=dev
|
|||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_USER=postgres
|
DB_USER=postgres
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
DB_PASSWORD=admin
|
DB_PASSWORD=quick
|
||||||
DB_NAME=QuickGpt
|
DB_NAME=QuickGpt
|
||||||
|
|
||||||
SUPER_ADMIN_EMAIL=superadmin@email.com
|
SUPER_ADMIN_EMAIL=superadmin@email.com
|
||||||
|
@ -16,6 +16,7 @@ from private_gpt.users.models.subscription import Subscription
|
|||||||
from private_gpt.users.models.company import Company
|
from private_gpt.users.models.company import Company
|
||||||
from private_gpt.users.models.document import Document
|
from private_gpt.users.models.document import Document
|
||||||
from private_gpt.users.models.department import Department
|
from private_gpt.users.models.department import Department
|
||||||
|
from private_gpt.users.models.audit import Audit
|
||||||
# this is the Alembic Config object, which provides
|
# this is the Alembic Config object, which provides
|
||||||
# access to the values within the .ini file in use.
|
# access to the values within the .ini file in use.
|
||||||
config = context.config
|
config = context.config
|
||||||
|
@ -1,116 +0,0 @@
|
|||||||
"""Create models
|
|
||||||
|
|
||||||
Revision ID: 0aeaf9df35a6
|
|
||||||
Revises:
|
|
||||||
Create Date: 2024-02-20 19:16:15.608391
|
|
||||||
|
|
||||||
"""
|
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision: str = '0aeaf9df35a6'
|
|
||||||
down_revision: Union[str, None] = None
|
|
||||||
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_table('companies',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('name', sa.String(), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_companies_id'), 'companies', ['id'], unique=False)
|
|
||||||
op.create_index(op.f('ix_companies_name'), 'companies', ['name'], unique=True)
|
|
||||||
op.create_table('roles',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('name', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('description', sa.Text(), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_roles_id'), 'roles', ['id'], unique=False)
|
|
||||||
op.create_index(op.f('ix_roles_name'), 'roles', ['name'], unique=False)
|
|
||||||
op.create_table('departments',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('name', sa.String(), nullable=True),
|
|
||||||
sa.Column('company_id', sa.Integer(), nullable=True),
|
|
||||||
sa.ForeignKeyConstraint(['company_id'], ['companies.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_departments_id'), 'departments', ['id'], unique=False)
|
|
||||||
op.create_index(op.f('ix_departments_name'), 'departments', ['name'], unique=True)
|
|
||||||
op.create_table('subscriptions',
|
|
||||||
sa.Column('sub_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('company_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('start_date', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('end_date', sa.DateTime(), nullable=True),
|
|
||||||
sa.ForeignKeyConstraint(['company_id'], ['companies.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('sub_id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_subscriptions_sub_id'), 'subscriptions', ['sub_id'], unique=False)
|
|
||||||
op.create_table('users',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('email', sa.String(length=225), nullable=False),
|
|
||||||
sa.Column('hashed_password', sa.String(), nullable=False),
|
|
||||||
sa.Column('fullname', sa.String(length=225), nullable=False),
|
|
||||||
sa.Column('is_active', sa.Boolean(), nullable=True),
|
|
||||||
sa.Column('last_login', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('company_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('department_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['company_id'], ['companies.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['department_id'], ['departments.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('id'),
|
|
||||||
sa.UniqueConstraint('email'),
|
|
||||||
sa.UniqueConstraint('fullname'),
|
|
||||||
sa.UniqueConstraint('fullname', name='unique_username_no_spacing')
|
|
||||||
)
|
|
||||||
op.create_table('document',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('filename', sa.String(length=225), nullable=False),
|
|
||||||
sa.Column('uploaded_by', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('uploaded_at', sa.DateTime(), nullable=False),
|
|
||||||
sa.Column('department_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['department_id'], ['departments.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['uploaded_by'], ['users.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('id'),
|
|
||||||
sa.UniqueConstraint('filename')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_document_id'), 'document', ['id'], unique=False)
|
|
||||||
op.create_table('user_roles',
|
|
||||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('role_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('company_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['company_id'], ['companies.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['role_id'], ['roles.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('user_id', 'role_id', 'company_id'),
|
|
||||||
sa.UniqueConstraint('user_id', 'role_id', 'company_id', name='unique_user_role')
|
|
||||||
)
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_table('user_roles')
|
|
||||||
op.drop_index(op.f('ix_document_id'), table_name='document')
|
|
||||||
op.drop_table('document')
|
|
||||||
op.drop_table('users')
|
|
||||||
op.drop_index(op.f('ix_subscriptions_sub_id'), table_name='subscriptions')
|
|
||||||
op.drop_table('subscriptions')
|
|
||||||
op.drop_index(op.f('ix_departments_name'), table_name='departments')
|
|
||||||
op.drop_index(op.f('ix_departments_id'), table_name='departments')
|
|
||||||
op.drop_table('departments')
|
|
||||||
op.drop_index(op.f('ix_roles_name'), table_name='roles')
|
|
||||||
op.drop_index(op.f('ix_roles_id'), table_name='roles')
|
|
||||||
op.drop_table('roles')
|
|
||||||
op.drop_index(op.f('ix_companies_name'), table_name='companies')
|
|
||||||
op.drop_index(op.f('ix_companies_id'), table_name='companies')
|
|
||||||
op.drop_table('companies')
|
|
||||||
# ### end Alembic commands ###
|
|
52
alembic/versions/62c1a29320fc_updated_audit_log.py
Normal file
52
alembic/versions/62c1a29320fc_updated_audit_log.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"""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 ###
|
@ -1,118 +0,0 @@
|
|||||||
"""Create model
|
|
||||||
|
|
||||||
Revision ID: 8c4bd1aaf45a
|
|
||||||
Revises:
|
|
||||||
Create Date: 2024-02-22 13:19:29.947241
|
|
||||||
|
|
||||||
"""
|
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision: str = '8c4bd1aaf45a'
|
|
||||||
down_revision: Union[str, None] = None
|
|
||||||
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_table('companies',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('name', sa.String(), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_companies_id'), 'companies', ['id'], unique=False)
|
|
||||||
op.create_index(op.f('ix_companies_name'), 'companies', ['name'], unique=True)
|
|
||||||
op.create_table('roles',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('name', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('description', sa.Text(), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_roles_id'), 'roles', ['id'], unique=False)
|
|
||||||
op.create_index(op.f('ix_roles_name'), 'roles', ['name'], unique=False)
|
|
||||||
op.create_table('departments',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('name', sa.String(), nullable=True),
|
|
||||||
sa.Column('company_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('total_users', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('total_documents', sa.Integer(), nullable=True),
|
|
||||||
sa.ForeignKeyConstraint(['company_id'], ['companies.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_departments_id'), 'departments', ['id'], unique=False)
|
|
||||||
op.create_index(op.f('ix_departments_name'), 'departments', ['name'], unique=True)
|
|
||||||
op.create_table('subscriptions',
|
|
||||||
sa.Column('sub_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('company_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('start_date', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('end_date', sa.DateTime(), nullable=True),
|
|
||||||
sa.ForeignKeyConstraint(['company_id'], ['companies.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('sub_id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_subscriptions_sub_id'), 'subscriptions', ['sub_id'], unique=False)
|
|
||||||
op.create_table('users',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('email', sa.String(length=225), nullable=False),
|
|
||||||
sa.Column('hashed_password', sa.String(), nullable=False),
|
|
||||||
sa.Column('fullname', sa.String(length=225), nullable=False),
|
|
||||||
sa.Column('is_active', sa.Boolean(), nullable=True),
|
|
||||||
sa.Column('last_login', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('company_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('department_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['company_id'], ['companies.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['department_id'], ['departments.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('id'),
|
|
||||||
sa.UniqueConstraint('email'),
|
|
||||||
sa.UniqueConstraint('fullname'),
|
|
||||||
sa.UniqueConstraint('fullname', name='unique_username_no_spacing')
|
|
||||||
)
|
|
||||||
op.create_table('document',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('filename', sa.String(length=225), nullable=False),
|
|
||||||
sa.Column('uploaded_by', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('uploaded_at', sa.DateTime(), nullable=False),
|
|
||||||
sa.Column('department_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['department_id'], ['departments.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['uploaded_by'], ['users.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('id'),
|
|
||||||
sa.UniqueConstraint('filename')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_document_id'), 'document', ['id'], unique=False)
|
|
||||||
op.create_table('user_roles',
|
|
||||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('role_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('company_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['company_id'], ['companies.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['role_id'], ['roles.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('user_id', 'role_id', 'company_id'),
|
|
||||||
sa.UniqueConstraint('user_id', 'role_id', 'company_id', name='unique_user_role')
|
|
||||||
)
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_table('user_roles')
|
|
||||||
op.drop_index(op.f('ix_document_id'), table_name='document')
|
|
||||||
op.drop_table('document')
|
|
||||||
op.drop_table('users')
|
|
||||||
op.drop_index(op.f('ix_subscriptions_sub_id'), table_name='subscriptions')
|
|
||||||
op.drop_table('subscriptions')
|
|
||||||
op.drop_index(op.f('ix_departments_name'), table_name='departments')
|
|
||||||
op.drop_index(op.f('ix_departments_id'), table_name='departments')
|
|
||||||
op.drop_table('departments')
|
|
||||||
op.drop_index(op.f('ix_roles_name'), table_name='roles')
|
|
||||||
op.drop_index(op.f('ix_roles_id'), table_name='roles')
|
|
||||||
op.drop_table('roles')
|
|
||||||
op.drop_index(op.f('ix_companies_name'), table_name='companies')
|
|
||||||
op.drop_index(op.f('ix_companies_id'), table_name='companies')
|
|
||||||
op.drop_table('companies')
|
|
||||||
# ### end Alembic commands ###
|
|
50
alembic/versions/b526c0676007_audit_log_update.py
Normal file
50
alembic/versions/b526c0676007_audit_log_update.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
"""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 ###
|
@ -1,8 +1,8 @@
|
|||||||
"""update department model
|
"""Audit log
|
||||||
|
|
||||||
Revision ID: 36beb9b73c64
|
Revision ID: eb0a7f182c75
|
||||||
Revises: 0aeaf9df35a6
|
Revises:
|
||||||
Create Date: 2024-02-21 15:12:07.840057
|
Create Date: 2024-02-24 11:37:43.713632
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
@ -12,16 +12,23 @@ import sqlalchemy as sa
|
|||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '36beb9b73c64'
|
revision: str = 'eb0a7f182c75'
|
||||||
down_revision: Union[str, None] = '0aeaf9df35a6'
|
down_revision: Union[str, None] = None
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.add_column('departments', sa.Column('total_users', sa.Integer(), nullable=True))
|
op.create_table('audit',
|
||||||
op.add_column('departments', sa.Column('total_documents', sa.Integer(), nullable=True))
|
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.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.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
@ -29,6 +36,6 @@ def upgrade() -> None:
|
|||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
|
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
|
||||||
op.drop_column('departments', 'total_documents')
|
op.drop_index(op.f('ix_audit_id'), table_name='audit')
|
||||||
op.drop_column('departments', 'total_users')
|
op.drop_table('audit')
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
@ -1,5 +1,10 @@
|
|||||||
# start a fastapi server with uvicorn
|
# start a fastapi server with uvicorn
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from fastapi.middleware import Middleware
|
||||||
|
from private_gpt.users.db.session import SessionLocal
|
||||||
|
from private_gpt.users.models import Audit, User, Department, Document
|
||||||
|
from private_gpt.users.api.deps import get_audit_logger, get_db
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
from private_gpt.main import app
|
from private_gpt.main import app
|
||||||
@ -10,6 +15,7 @@ from private_gpt.constants import UPLOAD_DIR
|
|||||||
# Set log_config=None to do not use the uvicorn logging configuration, and
|
# Set log_config=None to do not use the uvicorn logging configuration, and
|
||||||
# use ours instead. For reference, see below:
|
# use ours instead. For reference, see below:
|
||||||
# https://github.com/tiangolo/fastapi/discussions/7457#discussioncomment-5141108
|
# https://github.com/tiangolo/fastapi/discussions/7457#discussioncomment-5141108
|
||||||
|
|
||||||
app.mount("/static", StaticFiles(directory=UPLOAD_DIR), name="static")
|
app.mount("/static", StaticFiles(directory=UPLOAD_DIR), name="static")
|
||||||
|
|
||||||
uvicorn.run(app, host="0.0.0.0", port=settings().server.port, log_config=None)
|
uvicorn.run(app, host="0.0.0.0", port=settings().server.port, log_config=None)
|
||||||
|
@ -131,6 +131,7 @@ def delete_ingested(request: Request, doc_id: str) -> None:
|
|||||||
def delete_file(
|
def delete_file(
|
||||||
request: Request,
|
request: Request,
|
||||||
delete_input: DeleteFilename,
|
delete_input: DeleteFilename,
|
||||||
|
log_audit: models.Audit = Depends(deps.get_audit_logger),
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
current_user: models.User = Security(
|
current_user: models.User = Security(
|
||||||
deps.get_current_user,
|
deps.get_current_user,
|
||||||
@ -159,9 +160,12 @@ def delete_file(
|
|||||||
print("Unable to delete file from the static directory")
|
print("Unable to delete file from the static directory")
|
||||||
document = crud.documents.get_by_filename(db,file_name=filename)
|
document = crud.documents.get_by_filename(db,file_name=filename)
|
||||||
if document:
|
if document:
|
||||||
|
log_audit(model='Document', action='delete',
|
||||||
|
details={"status": "SUCCESS", "message": f"{filename}' successfully deleted."}, user_id=current_user.id)
|
||||||
crud.documents.remove(db=db, id=document.id)
|
crud.documents.remove(db=db, id=document.id)
|
||||||
return {"status": "SUCCESS", "message": f"{filename}' successfully deleted."}
|
return {"status": "SUCCESS", "message": f"{filename}' successfully deleted."}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(traceback.print_exc())
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Unexpected error deleting documents with filename '{filename}': {str(e)}")
|
f"Unexpected error deleting documents with filename '{filename}': {str(e)}")
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@ -171,6 +175,8 @@ def delete_file(
|
|||||||
@ingest_router.post("/ingest/file", response_model=IngestResponse, tags=["Ingestion"])
|
@ingest_router.post("/ingest/file", response_model=IngestResponse, tags=["Ingestion"])
|
||||||
def ingest_file(
|
def ingest_file(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
log_audit: models.Audit = Depends(deps.get_audit_logger),
|
||||||
|
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
file: UploadFile = File(...),
|
file: UploadFile = File(...),
|
||||||
current_user: models.User = Security(
|
current_user: models.User = Security(
|
||||||
@ -210,12 +216,23 @@ def ingest_file(
|
|||||||
with open(upload_path, "rb") as f:
|
with open(upload_path, "rb") as f:
|
||||||
ingested_documents = service.ingest_bin_data(file.filename, f)
|
ingested_documents = service.ingest_bin_data(file.filename, f)
|
||||||
logger.info(f"{file.filename} is uploaded by the {current_user.fullname}.")
|
logger.info(f"{file.filename} is uploaded by the {current_user.fullname}.")
|
||||||
|
response = IngestResponse(
|
||||||
return IngestResponse(object="list", model="private-gpt", data=ingested_documents)
|
object="list", model="private-gpt", data=ingested_documents)
|
||||||
|
log_audit(model='Document', action='create',
|
||||||
|
details={
|
||||||
|
'status': '200',
|
||||||
|
'filename': file.filename,
|
||||||
|
'user': current_user.fullname,
|
||||||
|
}, user_id=current_user.id)
|
||||||
|
return response
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
|
print(traceback.print_exc())
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(traceback.print_exc())
|
||||||
|
log_audit(model='Document', action='create',
|
||||||
|
details={"status": 500, "detail": "Internal Server Error: Unable to ingest file.", }, user_id=current_user.id)
|
||||||
logger.error(f"There was an error uploading the file(s): {str(e)}")
|
logger.error(f"There was an error uploading the file(s): {str(e)}")
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@ -225,11 +242,13 @@ def ingest_file(
|
|||||||
|
|
||||||
async def common_ingest_logic(
|
async def common_ingest_logic(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
||||||
db: Session,
|
db: Session,
|
||||||
ocr_file,
|
ocr_file,
|
||||||
current_user,
|
current_user,
|
||||||
):
|
):
|
||||||
service = request.state.injector.get(IngestService)
|
service = request.state.injector.get(IngestService)
|
||||||
|
log_audit: models.Audit = Depends(deps.get_audit_logger)
|
||||||
try:
|
try:
|
||||||
with open(ocr_file, 'rb') as file:
|
with open(ocr_file, 'rb') as file:
|
||||||
file_name = Path(ocr_file).name
|
file_name = Path(ocr_file).name
|
||||||
@ -257,6 +276,8 @@ async def common_ingest_logic(
|
|||||||
f.write(file.read())
|
f.write(file.read())
|
||||||
file.seek(0) # Move the file pointer back to the beginning
|
file.seek(0) # Move the file pointer back to the beginning
|
||||||
ingested_documents = service.ingest_bin_data(file_name, file)
|
ingested_documents = service.ingest_bin_data(file_name, file)
|
||||||
|
log_audit(model='Document', action='create',
|
||||||
|
details={'status': 200, 'message': "file uploaded successfully."}, user_id=current_user.id)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{file_name} is uploaded by the {current_user.fullname}.")
|
f"{file_name} is uploaded by the {current_user.fullname}.")
|
||||||
@ -264,10 +285,13 @@ async def common_ingest_logic(
|
|||||||
return ingested_documents
|
return ingested_documents
|
||||||
|
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
|
print(traceback.print_exc())
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"There was an error uploading the file(s): {str(e)}")
|
print(traceback.print_exc())
|
||||||
|
log_audit(model='Document', action='create',
|
||||||
|
details={"status": 500, "detail": "Internal Server Error: Unable to ingest file.", }, user_id=current_user.id)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=500,
|
status_code=500,
|
||||||
detail="Internal Server Error: Unable to ingest file.",
|
detail="Internal Server Error: Unable to ingest file.",
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from fastapi import Request, Depends, HTTPException
|
||||||
import logging
|
import logging
|
||||||
from private_gpt.users.core.config import settings
|
from private_gpt.users.core.config import settings
|
||||||
from private_gpt.users.constants.role import Role
|
from private_gpt.users.constants.role import Role
|
||||||
@ -5,7 +6,7 @@ from typing import Union, Any, Generator
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from private_gpt.users import crud, models, schemas
|
from private_gpt.users import crud, models, schemas
|
||||||
from private_gpt.users.db.session import SessionLocal
|
from private_gpt.users.db.session import SessionLocal
|
||||||
from fastapi import Depends, HTTPException, status
|
from fastapi import Depends, HTTPException, Request, status
|
||||||
from fastapi.security import OAuth2PasswordBearer, SecurityScopes
|
from fastapi.security import OAuth2PasswordBearer, SecurityScopes
|
||||||
from private_gpt.users.core.security import (
|
from private_gpt.users.core.security import (
|
||||||
ALGORITHM,
|
ALGORITHM,
|
||||||
@ -13,6 +14,7 @@ from private_gpt.users.core.security import (
|
|||||||
)
|
)
|
||||||
from fastapi import Depends, HTTPException, Security, status
|
from fastapi import Depends, HTTPException, Security, status
|
||||||
from jose import jwt
|
from jose import jwt
|
||||||
|
from private_gpt.users.utils.audit import log_audit_entry
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from private_gpt.users.constants.role import Role
|
from private_gpt.users.constants.role import Role
|
||||||
from private_gpt.users.schemas.token import TokenPayload
|
from private_gpt.users.schemas.token import TokenPayload
|
||||||
@ -130,3 +132,10 @@ def get_active_subscription(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN,
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
detail="Access Forbidden - No Active Subscription",
|
detail="Access Forbidden - No Active Subscription",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_audit_logger(request: Request, db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
return lambda model, action, details, user_id=None: log_audit_entry(db, model, action, details, user_id)
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=f"Error in get_audit_logger: {str(e)}")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from private_gpt.users.api.v1.routers import auth, roles, user_roles, users, subscriptions, companies, departments, documents
|
from private_gpt.users.api.v1.routers import auth, roles, user_roles, users, subscriptions, companies, departments, documents, audits
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
api_router = APIRouter(prefix="/v1")
|
api_router = APIRouter(prefix="/v1")
|
||||||
@ -11,4 +11,5 @@ api_router.include_router(companies.router)
|
|||||||
api_router.include_router(subscriptions.router)
|
api_router.include_router(subscriptions.router)
|
||||||
api_router.include_router(departments.router)
|
api_router.include_router(departments.router)
|
||||||
api_router.include_router(documents.router)
|
api_router.include_router(documents.router)
|
||||||
|
api_router.include_router(audits.router)
|
||||||
|
|
||||||
|
30
private_gpt/users/api/v1/routers/audits.py
Normal file
30
private_gpt/users/api/v1/routers/audits.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from typing import Any, List
|
||||||
|
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, status, Security
|
||||||
|
|
||||||
|
from private_gpt.users.api import deps
|
||||||
|
from private_gpt.users.constants.role import Role
|
||||||
|
from private_gpt.users import crud, models, schemas
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/audit", tags=["Companies"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", response_model=List[schemas.Audit])
|
||||||
|
def list_companies(
|
||||||
|
db: Session = Depends(deps.get_db),
|
||||||
|
skip: int = 0,
|
||||||
|
limit: int = 100,
|
||||||
|
current_user: models.User = Security(
|
||||||
|
deps.get_current_user,
|
||||||
|
scopes=[Role.SUPER_ADMIN["name"]],
|
||||||
|
),
|
||||||
|
) -> List[schemas.Audit]:
|
||||||
|
"""
|
||||||
|
Retrieve a list of companies with pagination support.
|
||||||
|
"""
|
||||||
|
logs = crud.audit.get_multi(db, skip=skip, limit=limit)
|
||||||
|
return logs
|
@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
LDAP_SERVER = settings.LDAP_SERVER
|
LDAP_SERVER = settings.LDAP_SERVER
|
||||||
# LDAP_ENABLE = settings.LDAP_ENABLE
|
# LDAP_ENABLE = settings.LDAP_ENABLE
|
||||||
LDAP_ENABLE = True
|
LDAP_ENABLE = False
|
||||||
|
|
||||||
router = APIRouter(prefix="/auth", tags=["auth"])
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||||
|
|
||||||
@ -108,6 +108,7 @@ def ad_user_register(
|
|||||||
|
|
||||||
@router.post("/login", response_model=schemas.TokenSchema)
|
@router.post("/login", response_model=schemas.TokenSchema)
|
||||||
def login_access_token(
|
def login_access_token(
|
||||||
|
log_audit: models.Audit = Depends(deps.get_audit_logger),
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
form_data: OAuth2PasswordRequestForm = Depends(),
|
form_data: OAuth2PasswordRequestForm = Depends(),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
@ -168,6 +169,8 @@ def login_access_token(
|
|||||||
"user": token_payload,
|
"user": token_payload,
|
||||||
"token_type": "bearer",
|
"token_type": "bearer",
|
||||||
}
|
}
|
||||||
|
log_audit(model='User', action='update',
|
||||||
|
details=token_payload, user_id=user.id)
|
||||||
return JSONResponse(content=response_dict)
|
return JSONResponse(content=response_dict)
|
||||||
|
|
||||||
|
|
||||||
@ -197,6 +200,8 @@ def refresh_access_token(
|
|||||||
@router.post("/register", response_model=schemas.TokenSchema)
|
@router.post("/register", response_model=schemas.TokenSchema)
|
||||||
def register(
|
def register(
|
||||||
*,
|
*,
|
||||||
|
log_audit: models.Audit = Depends(deps.get_audit_logger),
|
||||||
|
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
email: str = Body(...),
|
email: str = Body(...),
|
||||||
fullname: str = Body(...),
|
fullname: str = Body(...),
|
||||||
@ -218,6 +223,8 @@ def register(
|
|||||||
|
|
||||||
existing_user = crud.user.get_by_email(db, email=email)
|
existing_user = crud.user.get_by_email(db, email=email)
|
||||||
if existing_user:
|
if existing_user:
|
||||||
|
log_audit(model='User', action='creation',
|
||||||
|
details={"status": '409', 'detail': "The user with this email already exists!", }, user_id=current_user.id)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=409,
|
status_code=409,
|
||||||
detail="The user with this email already exists!",
|
detail="The user with this email already exists!",
|
||||||
@ -245,6 +252,8 @@ def register(
|
|||||||
)
|
)
|
||||||
user_role_name = role_name or Role.GUEST["name"]
|
user_role_name = role_name or Role.GUEST["name"]
|
||||||
user_role = create_user_role(db, user, user_role_name, company)
|
user_role = create_user_role(db, user, user_role_name, company)
|
||||||
|
log_audit(model='user_roles', action='creation',
|
||||||
|
details={"status": '201', 'detail': "User role created successfully.", }, user_id=current_user.id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(traceback.format_exc())
|
print(traceback.format_exc())
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@ -258,4 +267,7 @@ def register(
|
|||||||
"token_type": "bearer",
|
"token_type": "bearer",
|
||||||
"password": random_password,
|
"password": random_password,
|
||||||
}
|
}
|
||||||
|
log_audit(model='User', action='creation',
|
||||||
|
details={"status": '201', 'detail': "User created successfully.", }, user_id=current_user.id)
|
||||||
|
|
||||||
return JSONResponse(content=response_dict, status_code=status.HTTP_201_CREATED)
|
return JSONResponse(content=response_dict, status_code=status.HTTP_201_CREATED)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from typing import Any, List
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
@ -9,28 +10,47 @@ from private_gpt.users.api import deps
|
|||||||
from private_gpt.users.constants.role import Role
|
from private_gpt.users.constants.role import Role
|
||||||
from private_gpt.users import crud, models, schemas
|
from private_gpt.users import crud, models, schemas
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
router = APIRouter(prefix="/departments", tags=["Departments"])
|
router = APIRouter(prefix="/departments", tags=["Departments"])
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=List[schemas.Department])
|
def log_audit_department(
|
||||||
def list_deparments(
|
db: Session,
|
||||||
|
current_user: models.User,
|
||||||
|
action: str,
|
||||||
|
details: dict
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
audit_entry = models.Audit(
|
||||||
|
user_id=current_user.id,
|
||||||
|
model='Department',
|
||||||
|
action=action,
|
||||||
|
details=details,
|
||||||
|
)
|
||||||
|
db.add(audit_entry)
|
||||||
|
db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", response_model=list[schemas.Department])
|
||||||
|
def list_departments(
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
current_user: models.User = Security(
|
current_user: models.User = Security(
|
||||||
deps.get_current_user,
|
deps.get_current_user,
|
||||||
),
|
),
|
||||||
) -> List[schemas.Department]:
|
) -> list[schemas.Department]:
|
||||||
"""
|
"""
|
||||||
Retrieve a list of department with pagination support.
|
Retrieve a list of departments with pagination support.
|
||||||
"""
|
"""
|
||||||
deparments = crud.department.get_multi(db, skip=skip, limit=limit)
|
departments = crud.department.get_multi(db, skip=skip, limit=limit)
|
||||||
return deparments
|
return departments
|
||||||
|
|
||||||
|
|
||||||
@router.post("/create", response_model=schemas.Department)
|
@router.post("/create", response_model=schemas.Department)
|
||||||
def create_deparment(
|
def create_department(
|
||||||
department_in: schemas.DepartmentCreate,
|
department_in: schemas.DepartmentCreate,
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
current_user: models.User = Security(
|
current_user: models.User = Security(
|
||||||
@ -41,18 +61,35 @@ def create_deparment(
|
|||||||
"""
|
"""
|
||||||
Create a new department
|
Create a new department
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
company_id = current_user.company_id
|
company_id = current_user.company_id
|
||||||
department_create_in = schemas.DepartmentAdminCreate(name=department_in.name, company_id=company_id)
|
department_create_in = schemas.DepartmentAdminCreate(
|
||||||
|
name=department_in.name, company_id=company_id)
|
||||||
department = crud.department.create(db=db, obj_in=department_create_in)
|
department = crud.department.create(db=db, obj_in=department_create_in)
|
||||||
department = jsonable_encoder(department)
|
department1 = jsonable_encoder(department)
|
||||||
|
|
||||||
|
details = {
|
||||||
|
'user_id': current_user.id,
|
||||||
|
'department_id': department.id,
|
||||||
|
'department_name': department.name
|
||||||
|
}
|
||||||
|
|
||||||
|
log_audit_department(db, current_user, 'create', details)
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_201_CREATED,
|
status_code=status.HTTP_201_CREATED,
|
||||||
content={
|
content={
|
||||||
"message": "Department created successfully",
|
"message": "Department created successfully",
|
||||||
"department": department
|
"department": department1
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
logger.error(f"Error creating department: {str(e)}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail="Internal Server Error",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/read", response_model=schemas.Department)
|
@router.post("/read", response_model=schemas.Department)
|
||||||
@ -67,10 +104,28 @@ def read_department(
|
|||||||
"""
|
"""
|
||||||
Read a Department by ID
|
Read a Department by ID
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
department = crud.department.get_by_id(db, id=department_id)
|
department = crud.department.get_by_id(db, id=department_id)
|
||||||
if department is None:
|
if department is None:
|
||||||
raise HTTPException(status_code=404, detail="department not found")
|
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
|
return department
|
||||||
|
except Exception as e:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
logger.error(f"Error reading department: {str(e)}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail="Internal Server Error",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/update", response_model=schemas.Department)
|
@router.post("/update", response_model=schemas.Department)
|
||||||
@ -85,20 +140,40 @@ def update_department(
|
|||||||
"""
|
"""
|
||||||
Update a Department by ID
|
Update a Department by ID
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
department = crud.department.get_by_id(db, id=department_in.id)
|
department = crud.department.get_by_id(db, id=department_in.id)
|
||||||
|
old_name = department.name
|
||||||
if department is None:
|
if department is None:
|
||||||
raise HTTPException(status_code=404, detail="department not found")
|
raise HTTPException(status_code=404, detail="Department not found")
|
||||||
|
|
||||||
updated_department = crud.department.update(
|
updated_department = crud.department.update(
|
||||||
db=db, db_obj=department, obj_in=department_in)
|
db=db, db_obj=department, obj_in=department_in)
|
||||||
updated_department = jsonable_encoder(updated_department)
|
updated_department = jsonable_encoder(updated_department)
|
||||||
|
|
||||||
|
details = {
|
||||||
|
'status': '200',
|
||||||
|
'user_id': current_user.id,
|
||||||
|
'department_id': department.id,
|
||||||
|
'old_department_name': old_name,
|
||||||
|
'new_department_name': department.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
log_audit_department(db, current_user, 'update', details)
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_200_OK,
|
status_code=status.HTTP_200_OK,
|
||||||
content={
|
content={
|
||||||
"message": f"{department_in} department updated successfully",
|
"message": f"Department updated successfully",
|
||||||
"department": updated_department
|
"department": updated_department
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
logger.error(f"Error updating department: {str(e)}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail="Internal Server Error",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/delete", response_model=schemas.Department)
|
@router.post("/delete", response_model=schemas.Department)
|
||||||
@ -113,19 +188,36 @@ def delete_department(
|
|||||||
"""
|
"""
|
||||||
Delete a Department by ID
|
Delete a Department by ID
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
department_id = department_in.id
|
department_id = department_in.id
|
||||||
department = crud.department.get(db, id=department_id)
|
department = crud.department.get(db, id=department_id)
|
||||||
if department is None:
|
if department is None:
|
||||||
raise HTTPException(status_code=404, detail="User not found")
|
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, 'delete', details)
|
||||||
|
|
||||||
|
deleted_department = crud.department.remove(db=db, id=department_id)
|
||||||
|
deleted_department = jsonable_encoder(deleted_department)
|
||||||
|
|
||||||
|
|
||||||
department = crud.department.remove(db=db, id=department_id)
|
|
||||||
if department is None:
|
|
||||||
raise HTTPException(status_code=404, detail="department not found")
|
|
||||||
department = jsonable_encoder(department)
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_200_OK,
|
status_code=status.HTTP_200_OK,
|
||||||
content={
|
content={
|
||||||
"message": "Department deleted successfully",
|
"message": "Department deleted successfully",
|
||||||
"deparment": department,
|
"department": deleted_department,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
logger.error(f"Error deleting department: {str(e)}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail="Internal Server Error",
|
||||||
|
)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import traceback
|
||||||
from typing import Any, List, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@ -14,6 +15,26 @@ from private_gpt.users.core.security import verify_password, get_password_hash
|
|||||||
|
|
||||||
router = APIRouter(prefix="/users", tags=["users"])
|
router = APIRouter(prefix="/users", tags=["users"])
|
||||||
|
|
||||||
|
|
||||||
|
def log_audit_user(
|
||||||
|
db: Session,
|
||||||
|
current_user: models.User,
|
||||||
|
action: str,
|
||||||
|
details: dict
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
audit_entry = models.Audit(
|
||||||
|
user_id=current_user.id,
|
||||||
|
model='User',
|
||||||
|
action=action,
|
||||||
|
details=details,
|
||||||
|
)
|
||||||
|
db.add(audit_entry)
|
||||||
|
db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=List[schemas.User])
|
@router.get("", response_model=List[schemas.User])
|
||||||
def read_users(
|
def read_users(
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
@ -78,6 +99,17 @@ def create_user(
|
|||||||
detail="The user with this email already exists in the system.",
|
detail="The user with this email already exists in the system.",
|
||||||
)
|
)
|
||||||
user = crud.user.create(db, obj_in=user_in)
|
user = crud.user.create(db, obj_in=user_in)
|
||||||
|
|
||||||
|
details = {
|
||||||
|
'admin_id': current_user.id,
|
||||||
|
'user_id': user.id,
|
||||||
|
'email': user.email,
|
||||||
|
'fullname': user.fullname,
|
||||||
|
'company_id': user.company_id,
|
||||||
|
'department_id': user.department_id,
|
||||||
|
}
|
||||||
|
log_audit_user(db, current_user, 'create', details)
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_201_CREATED,
|
status_code=status.HTTP_201_CREATED,
|
||||||
content={"message": "User created successfully", "user": jsonable_encoder(user)},
|
content={"message": "User created successfully", "user": jsonable_encoder(user)},
|
||||||
@ -102,6 +134,14 @@ def update_username(
|
|||||||
company_id=user.company_id,
|
company_id=user.company_id,
|
||||||
department_id=user.department_id,
|
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,
|
||||||
|
}
|
||||||
|
log_audit_user(db, current_user, 'update_username', details)
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_200_OK,
|
status_code=status.HTTP_200_OK,
|
||||||
content={"message": "Username updated successfully",
|
content={"message": "Username updated successfully",
|
||||||
@ -159,6 +199,15 @@ def change_password(
|
|||||||
department_id=current_user.department_id,
|
department_id=current_user.department_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
details = {
|
||||||
|
'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)
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_200_OK,
|
status_code=status.HTTP_200_OK,
|
||||||
content={"message": "Password changed successfully", "user": jsonable_encoder(user_data)},
|
content={"message": "Password changed successfully", "user": jsonable_encoder(user_data)},
|
||||||
@ -201,6 +250,7 @@ def update_user(
|
|||||||
Update a user.
|
Update a user.
|
||||||
"""
|
"""
|
||||||
user = crud.user.get(db, id=user_id)
|
user = crud.user.get(db, id=user_id)
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
@ -214,6 +264,14 @@ def update_user(
|
|||||||
company_id=user.company_id,
|
company_id=user.company_id,
|
||||||
department_id=user.department_id,
|
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,
|
||||||
|
}
|
||||||
|
log_audit_user(db, current_user, 'update user', details)
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_200_OK,
|
status_code=status.HTTP_200_OK,
|
||||||
content={"message": "User updated successfully", "user": jsonable_encoder(user_data)},
|
content={"message": "User updated successfully", "user": jsonable_encoder(user_data)},
|
||||||
@ -286,6 +344,18 @@ def delete_user(
|
|||||||
"""
|
"""
|
||||||
user_id = delete_user.id
|
user_id = delete_user.id
|
||||||
user = crud.user.get(db, id=user_id)
|
user = crud.user.get(db, id=user_id)
|
||||||
|
|
||||||
|
details = {
|
||||||
|
'admin_id': current_user.id,
|
||||||
|
'deleted_user_id': user.id,
|
||||||
|
'email': user.email,
|
||||||
|
'fullname': user.fullname,
|
||||||
|
'company_id': user.company_id,
|
||||||
|
'department_id': user.department_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
log_audit_user(db, current_user, 'delete', details)
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
raise HTTPException(status_code=404, detail="User not found")
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
crud.user.remove(db, id=user_id)
|
crud.user.remove(db, id=user_id)
|
||||||
@ -340,6 +410,17 @@ def admin_update_user(
|
|||||||
role = crud.user_role.update(db, db_obj=user_role, obj_in=role_in)
|
role = crud.user_role.update(db, db_obj=user_role, obj_in=role_in)
|
||||||
|
|
||||||
user_in = schemas.UserAdmin(fullname=user_update.fullname, department_id=user_update.department_id)
|
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)
|
crud.user.update(db, db_obj=existing_user, obj_in=user_in)
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
|
@ -8,7 +8,7 @@ SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://{username}:{password}@{host}:{p
|
|||||||
port='5432',
|
port='5432',
|
||||||
db_name='QuickGpt',
|
db_name='QuickGpt',
|
||||||
username='postgres',
|
username='postgres',
|
||||||
password="admin",
|
password="quick",
|
||||||
)
|
)
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
|
@ -61,3 +61,4 @@ def verify_refresh_token(token: str) -> Optional[Dict[str, Any]]:
|
|||||||
return payload
|
return payload
|
||||||
except JWTError:
|
except JWTError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -5,3 +5,4 @@ from .company_crud import company
|
|||||||
from .subscription_crud import subscription
|
from .subscription_crud import subscription
|
||||||
from .document_crud import documents
|
from .document_crud import documents
|
||||||
from .department_crud import department
|
from .department_crud import department
|
||||||
|
from .audit_crud import audit
|
14
private_gpt/users/crud/audit_crud.py
Normal file
14
private_gpt/users/crud/audit_crud.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from private_gpt.users.crud.base import CRUDBase
|
||||||
|
from private_gpt.users.models.audit import Audit
|
||||||
|
from private_gpt.users.schemas.audit import AuditCreate, AuditUpdate
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
|
||||||
|
class CRUDAudit(CRUDBase[Audit, AuditCreate, AuditUpdate]):
|
||||||
|
def get_by_id(self, db: Session, *, id: str) -> Optional[Audit]:
|
||||||
|
return db.query(self.model).filter(Audit.id == id).first()
|
||||||
|
|
||||||
|
|
||||||
|
audit = CRUDAudit(Audit)
|
@ -5,3 +5,4 @@ from .role import Role
|
|||||||
from .document import Document
|
from .document import Document
|
||||||
from .subscription import Subscription
|
from .subscription import Subscription
|
||||||
from .department import Department
|
from .department import Department
|
||||||
|
from .audit import Audit
|
18
private_gpt/users/models/audit.py
Normal file
18
private_gpt/users/models/audit.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
|
||||||
|
from private_gpt.users.db.base_class import Base
|
||||||
|
from sqlalchemy.dialects.postgresql import JSONB
|
||||||
|
|
||||||
|
|
||||||
|
class Audit(Base):
|
||||||
|
__tablename__ = "audit"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||||
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||||
|
model = Column(String, nullable=False)
|
||||||
|
action = Column(String, nullable=False)
|
||||||
|
details = Column(JSONB, nullable=True)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Audit(id={self.id}, timestamp={self.timestamp}, user_id={self.user_id}, model={self.model}, action={self.action}, details={self.details})>"
|
@ -3,8 +3,8 @@ from sqlalchemy.orm import relationship, Session
|
|||||||
from sqlalchemy import Column, Integer, String
|
from sqlalchemy import Column, Integer, String
|
||||||
|
|
||||||
from private_gpt.users.db.base_class import Base
|
from private_gpt.users.db.base_class import Base
|
||||||
from private_gpt.users.models.document import Document
|
# from private_gpt.users.models.document import Document
|
||||||
from private_gpt.users.models.user import User
|
# from private_gpt.users.models.user import User
|
||||||
|
|
||||||
|
|
||||||
class Department(Base):
|
class Department(Base):
|
||||||
@ -25,21 +25,25 @@ class Department(Base):
|
|||||||
total_documents = Column(Integer, default=0)
|
total_documents = Column(Integer, default=0)
|
||||||
|
|
||||||
|
|
||||||
|
# @event.listens_for(Department, 'after_insert')
|
||||||
def update_total_users(mapper, connection, target):
|
# @event.listens_for(Department, 'after_update')
|
||||||
session = Session(bind=connection)
|
# def update_total_users(mapper, connection, target):
|
||||||
target.total_users = session.query(User).filter_by(
|
# print("--------------------------------------------------------------Calling Event User------------------------------------------------------------------------")
|
||||||
department_id=target.id).count()
|
# connection.execute(
|
||||||
|
# Department.__table__.update().
|
||||||
|
# where(Department.id == target.id).
|
||||||
|
# values(total_users=Session.object_session(target).query(
|
||||||
|
# User).filter_by(department_id=target.id).count())
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
def update_total_documents(mapper, connection, target):
|
# @event.listens_for(Department, 'after_insert')
|
||||||
session = Session(bind=connection)
|
# @event.listens_for(Department, 'after_update')
|
||||||
target.total_documents = session.query(
|
# def update_total_documents(mapper, connection, target):
|
||||||
Document).filter_by(department_id=target.id).count()
|
# print("--------------------------------------------------------------Calling Event Department------------------------------------------------------------------------")
|
||||||
|
# connection.execute(
|
||||||
|
# Department.__table__.update().
|
||||||
# Attach event listeners to Department model
|
# where(Department.id == target.id).
|
||||||
event.listen(Department, 'after_insert', update_total_users)
|
# values(total_documents=Session.object_session(target).query(
|
||||||
event.listen(Department, 'after_update', update_total_users)
|
# Document).filter_by(department_id=target.id).count())
|
||||||
event.listen(Department, 'after_insert', update_total_documents)
|
# )
|
||||||
event.listen(Department, 'after_update', update_total_documents)
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
from private_gpt.users.db.base_class import Base
|
from private_gpt.users.db.base_class import Base
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from sqlalchemy import event, select, func, update
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
|
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
|
||||||
|
from private_gpt.users.models.department import Department
|
||||||
|
|
||||||
|
|
||||||
class Document(Base):
|
class Document(Base):
|
||||||
@ -25,6 +28,27 @@ class Document(Base):
|
|||||||
|
|
||||||
department_id = Column(Integer, ForeignKey(
|
department_id = Column(Integer, ForeignKey(
|
||||||
"departments.id"), nullable=False)
|
"departments.id"), nullable=False)
|
||||||
uploaded_by_user = relationship(
|
|
||||||
"User", back_populates="uploaded_documents")
|
|
||||||
department = relationship("Department", back_populates="documents")
|
department = relationship("Department", back_populates="documents")
|
||||||
|
|
||||||
|
|
||||||
|
@event.listens_for(Document, 'after_insert')
|
||||||
|
@event.listens_for(Document, 'after_delete')
|
||||||
|
def update_total_documents(mapper, connection, target):
|
||||||
|
department_id = target.department_id
|
||||||
|
print(f"Department ID is: {department_id}")
|
||||||
|
|
||||||
|
# Use SQLAlchemy's ORM constructs for better readability and maintainability:
|
||||||
|
total_documents = connection.execute(
|
||||||
|
select([func.count()]).select_from(Document).where(
|
||||||
|
Document.department_id == department_id)
|
||||||
|
).scalar()
|
||||||
|
|
||||||
|
print(f"Total documents is: {total_documents}")
|
||||||
|
print("Updating total documents")
|
||||||
|
|
||||||
|
# Use the correct update construct for SQLAlchemy:
|
||||||
|
connection.execute(
|
||||||
|
update(Department).values(total_documents=total_documents).where(
|
||||||
|
Department.id == department_id)
|
||||||
|
)
|
||||||
|
@ -9,9 +9,10 @@ from sqlalchemy import (
|
|||||||
DateTime,
|
DateTime,
|
||||||
ForeignKey
|
ForeignKey
|
||||||
)
|
)
|
||||||
|
from sqlalchemy import event, func, select, update
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from private_gpt.users.db.base_class import Base
|
from private_gpt.users.db.base_class import Base
|
||||||
|
from private_gpt.users.models.department import Department
|
||||||
class User(Base):
|
class User(Base):
|
||||||
"""Models a user table"""
|
"""Models a user table"""
|
||||||
__tablename__ = "users"
|
__tablename__ = "users"
|
||||||
@ -53,3 +54,21 @@ class User(Base):
|
|||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
UniqueConstraint('fullname', name='unique_username_no_spacing'),
|
UniqueConstraint('fullname', name='unique_username_no_spacing'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@event.listens_for(User, 'after_insert')
|
||||||
|
@event.listens_for(User, 'after_delete')
|
||||||
|
def update_total_users(mapper, connection, target):
|
||||||
|
department_id = target.department_id
|
||||||
|
print(f"Department ID is: {department_id}")
|
||||||
|
total_users = connection.execute(
|
||||||
|
select([func.count()]).select_from(User).where(
|
||||||
|
User.department_id == department_id)
|
||||||
|
).scalar()
|
||||||
|
print(f"Total users is: {total_users}")
|
||||||
|
connection.execute(
|
||||||
|
update(Department).values(total_users=total_users).where(
|
||||||
|
Department.id == department_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,3 +6,4 @@ from .subscription import Subscription, SubscriptionBase, SubscriptionCreate, Su
|
|||||||
from .company import Company, CompanyBase, CompanyCreate, CompanyUpdate
|
from .company import Company, CompanyBase, CompanyCreate, CompanyUpdate
|
||||||
from .documents import Document, DocumentCreate, DocumentsBase, DocumentUpdate, DocumentList
|
from .documents import Document, DocumentCreate, DocumentsBase, DocumentUpdate, DocumentList
|
||||||
from .department import Department, DepartmentCreate, DepartmentUpdate, DepartmentAdminCreate, DepartmentDelete
|
from .department import Department, DepartmentCreate, DepartmentUpdate, DepartmentAdminCreate, DepartmentDelete
|
||||||
|
from .audit import AuditBase, AuditCreate, AuditUpdate, Audit
|
30
private_gpt/users/schemas/audit.py
Normal file
30
private_gpt/users/schemas/audit.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class AuditBase(BaseModel):
|
||||||
|
id: int
|
||||||
|
model: str
|
||||||
|
user_id: int
|
||||||
|
action: str
|
||||||
|
details: dict
|
||||||
|
timestamp: Optional[datetime]
|
||||||
|
|
||||||
|
|
||||||
|
class AuditCreate(AuditBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AuditUpdate(AuditBase):
|
||||||
|
id: int
|
||||||
|
|
||||||
|
|
||||||
|
class AuditInDB(AuditBase):
|
||||||
|
id: int
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
class Audit(AuditBase):
|
||||||
|
pass
|
@ -7,7 +7,6 @@ class Ldap:
|
|||||||
self.server = ldap3.Server(server_uri, get_info=ldap3.ALL)
|
self.server = ldap3.Server(server_uri, get_info=ldap3.ALL)
|
||||||
print(f"Connected to ldap server: {self.server}")
|
print(f"Connected to ldap server: {self.server}")
|
||||||
self.conn = ldap3.Connection(self.server, user=ldap_user, password=ldap_pass, auto_bind=True)
|
self.conn = ldap3.Connection(self.server, user=ldap_user, password=ldap_pass, auto_bind=True)
|
||||||
print(self.conn)
|
|
||||||
|
|
||||||
def who_am_i(self):
|
def who_am_i(self):
|
||||||
return self.conn.extend.standard.who_am_i()
|
return self.conn.extend.standard.who_am_i()
|
||||||
|
24
private_gpt/users/utils/audit.py
Normal file
24
private_gpt/users/utils/audit.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from private_gpt.users.models.audit import Audit
|
||||||
|
|
||||||
|
def log_audit_entry(
|
||||||
|
session: Session,
|
||||||
|
model: str,
|
||||||
|
action: str,
|
||||||
|
details: dict,
|
||||||
|
user_id: int = None,
|
||||||
|
):
|
||||||
|
audit_entry = Audit(
|
||||||
|
timestamp=datetime.utcnow(),
|
||||||
|
user_id=user_id,
|
||||||
|
model=model,
|
||||||
|
action=action,
|
||||||
|
details=details,
|
||||||
|
)
|
||||||
|
|
||||||
|
session.add(audit_entry)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user