progress in permissions

This commit is contained in:
Hesabix 2025-09-19 14:34:43 +03:30
parent 33a2441cd9
commit c1da9cd0bd
8 changed files with 117 additions and 3 deletions

View file

@ -51,7 +51,7 @@ def register(request: Request, payload: RegisterRequest, db: Session = Depends(g
api_repo.create_session_key(user_id=user_id, key_hash=key_hash, device_id=payload.device_id, user_agent=user_agent, ip=ip, expires_at=None) api_repo.create_session_key(user_id=user_id, key_hash=key_hash, device_id=payload.device_id, user_agent=user_agent, ip=ip, expires_at=None)
from adapters.db.models.user import User from adapters.db.models.user import User
user_obj = db.get(User, user_id) user_obj = db.get(User, user_id)
user = {"id": user_id, "first_name": payload.first_name, "last_name": payload.last_name, "email": payload.email, "mobile": payload.mobile, "referral_code": getattr(user_obj, "referral_code", None)} user = {"id": user_id, "first_name": payload.first_name, "last_name": payload.last_name, "email": payload.email, "mobile": payload.mobile, "referral_code": getattr(user_obj, "referral_code", None), "app_permissions": getattr(user_obj, "app_permissions", None)}
response_data = {"api_key": api_key, "expires_at": None, "user": user} response_data = {"api_key": api_key, "expires_at": None, "user": user}
formatted_data = format_datetime_fields(response_data, request) formatted_data = format_datetime_fields(response_data, request)
return success_response(formatted_data, request) return success_response(formatted_data, request)

View file

@ -6,5 +6,6 @@ from .api_key import ApiKey # noqa: F401
from .captcha import Captcha # noqa: F401 from .captcha import Captcha # noqa: F401
from .password_reset import PasswordReset # noqa: F401 from .password_reset import PasswordReset # noqa: F401
from .business import Business # noqa: F401 from .business import Business # noqa: F401
from .business_permission import BusinessPermission # noqa: F401

View file

@ -0,0 +1,19 @@
from __future__ import annotations
from datetime import datetime
from sqlalchemy import Integer, ForeignKey, JSON, DateTime
from sqlalchemy.orm import Mapped, mapped_column
from adapters.db.session import Base
class BusinessPermission(Base):
__tablename__ = "business_permissions"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
business_id: Mapped[int] = mapped_column(Integer, ForeignKey("businesses.id", ondelete="CASCADE"), nullable=False, index=True)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
business_permissions: Mapped[dict | None] = mapped_column(JSON, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from sqlalchemy import String, DateTime, Boolean, Integer, ForeignKey from sqlalchemy import String, DateTime, Boolean, Integer, ForeignKey, JSON
from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.orm import Mapped, mapped_column
from adapters.db.session import Base from adapters.db.session import Base
@ -21,6 +21,8 @@ class User(Base):
# Marketing/Referral fields # Marketing/Referral fields
referral_code: Mapped[str] = mapped_column(String(32), unique=True, index=True, nullable=False) referral_code: Mapped[str] = mapped_column(String(32), unique=True, index=True, nullable=False)
referred_by_user_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True) referred_by_user_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
# App permissions
app_permissions: Mapped[dict | None] = mapped_column(JSON, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)

View file

@ -26,8 +26,26 @@ class UserRepository(BaseRepository[User]):
stmt = select(User).where(User.referral_code == referral_code) stmt = select(User).where(User.referral_code == referral_code)
return self.db.execute(stmt).scalars().first() return self.db.execute(stmt).scalars().first()
def is_first_user(self) -> bool:
"""بررسی اینکه آیا این اولین کاربر سیستم است یا نه"""
stmt = select(func.count()).select_from(User)
count = self.db.execute(stmt).scalar() or 0
return count == 0
def create(self, *, email: str | None, mobile: str | None, password_hash: str, first_name: str | None, last_name: str | None, referral_code: str, referred_by_user_id: int | None = None) -> User: def create(self, *, email: str | None, mobile: str | None, password_hash: str, first_name: str | None, last_name: str | None, referral_code: str, referred_by_user_id: int | None = None) -> User:
user = User(email=email, mobile=mobile, password_hash=password_hash, first_name=first_name, last_name=last_name, referral_code=referral_code, referred_by_user_id=referred_by_user_id) # تعیین دسترسی‌های برنامه بر اساس اینکه آیا کاربر اول است یا نه
app_permissions = {"superadmin": True} if self.is_first_user() else {}
user = User(
email=email,
mobile=mobile,
password_hash=password_hash,
first_name=first_name,
last_name=last_name,
referral_code=referral_code,
referred_by_user_id=referred_by_user_id,
app_permissions=app_permissions
)
self.db.add(user) self.db.add(user)
self.db.commit() self.db.commit()
self.db.refresh(user) self.db.refresh(user)
@ -85,6 +103,7 @@ class UserRepository(BaseRepository[User]):
"is_active": user.is_active, "is_active": user.is_active,
"referral_code": user.referral_code, "referral_code": user.referral_code,
"referred_by_user_id": user.referred_by_user_id, "referred_by_user_id": user.referred_by_user_id,
"app_permissions": user.app_permissions,
"created_at": user.created_at, "created_at": user.created_at,
"updated_at": user.updated_at, "updated_at": user.updated_at,
} }

View file

@ -12,6 +12,7 @@ adapters/db/session.py
adapters/db/models/__init__.py adapters/db/models/__init__.py
adapters/db/models/api_key.py adapters/db/models/api_key.py
adapters/db/models/business.py adapters/db/models/business.py
adapters/db/models/business_permission.py
adapters/db/models/captcha.py adapters/db/models/captcha.py
adapters/db/models/password_reset.py adapters/db/models/password_reset.py
adapters/db/models/user.py adapters/db/models/user.py
@ -52,6 +53,8 @@ migrations/env.py
migrations/versions/20250117_000003_add_business_table.py migrations/versions/20250117_000003_add_business_table.py
migrations/versions/20250117_000004_add_business_contact_fields.py migrations/versions/20250117_000004_add_business_contact_fields.py
migrations/versions/20250117_000005_add_business_geographic_fields.py migrations/versions/20250117_000005_add_business_geographic_fields.py
migrations/versions/20250117_000006_add_app_permissions_to_users.py
migrations/versions/20250117_000007_create_business_permissions_table.py
migrations/versions/20250915_000001_init_auth_tables.py migrations/versions/20250915_000001_init_auth_tables.py
migrations/versions/20250916_000002_add_referral_fields.py migrations/versions/20250916_000002_add_referral_fields.py
tests/__init__.py tests/__init__.py

View file

@ -0,0 +1,28 @@
"""add app permissions to users
Revision ID: 20250117_000006
Revises: 20250117_000005
Create Date: 2025-01-17 00:00:06.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '20250117_000006'
down_revision = '20250117_000005'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('app_permissions', sa.JSON(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'app_permissions')
# ### end Alembic commands ###

View file

@ -0,0 +1,42 @@
"""create business permissions table
Revision ID: 20250117_000007
Revises: 20250117_000006
Create Date: 2025-01-17 00:00:07.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '20250117_000007'
down_revision = '20250117_000006'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('business_permissions',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('business_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('business_permissions', sa.JSON(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['business_id'], ['businesses.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_business_permissions_business_id'), 'business_permissions', ['business_id'], unique=False)
op.create_index(op.f('ix_business_permissions_user_id'), 'business_permissions', ['user_id'], unique=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_business_permissions_user_id'), table_name='business_permissions')
op.drop_index(op.f('ix_business_permissions_business_id'), table_name='business_permissions')
op.drop_table('business_permissions')
# ### end Alembic commands ###