mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: Init explore metadata features & redesign org settings panel
This commit is contained in:
parent
87787724c4
commit
bfd27ef6e3
12 changed files with 1419 additions and 217 deletions
|
|
@ -0,0 +1,41 @@
|
|||
"""Organizations new model
|
||||
|
||||
Revision ID: 87a621284ae4
|
||||
Revises: 0314ec7791e1
|
||||
Create Date: 2024-12-17 22:51:50.998443
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa # noqa: F401
|
||||
import sqlmodel # noqa: F401
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '87a621284ae4'
|
||||
down_revision: Union[str, None] = '0314ec7791e1'
|
||||
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('organization', sa.Column('about', sqlmodel.sql.sqltypes.AutoString(), nullable=True))
|
||||
op.add_column('organization', sa.Column('socials', sa.JSON(), nullable=True))
|
||||
op.add_column('organization', sa.Column('links', sa.JSON(), nullable=True))
|
||||
op.add_column('organization', sa.Column('previews', sa.JSON(), nullable=True))
|
||||
op.add_column('organization', sa.Column('explore', sa.Boolean(), nullable=True))
|
||||
op.add_column('organization', sa.Column('label', sqlmodel.sql.sqltypes.AutoString(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('organization', 'label')
|
||||
op.drop_column('organization', 'explore')
|
||||
op.drop_column('organization', 'previews')
|
||||
op.drop_column('organization', 'links')
|
||||
op.drop_column('organization', 'socials')
|
||||
op.drop_column('organization', 'about')
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import Field, SQLModel
|
||||
from sqlmodel import Field, SQLModel, JSON, Column
|
||||
from src.db.roles import RoleRead
|
||||
|
||||
from src.db.organization_config import OrganizationConfig
|
||||
|
|
@ -9,10 +9,16 @@ from src.db.organization_config import OrganizationConfig
|
|||
class OrganizationBase(SQLModel):
|
||||
name: str
|
||||
description: Optional[str]
|
||||
slug: str
|
||||
email: str
|
||||
about: Optional[str]
|
||||
socials: Optional[dict] = Field(default={}, sa_column=Column(JSON))
|
||||
links: Optional[dict] = Field(default={}, sa_column=Column(JSON))
|
||||
logo_image: Optional[str]
|
||||
thumbnail_image: Optional[str]
|
||||
previews: Optional[dict] = Field(default={}, sa_column=Column(JSON))
|
||||
explore: Optional[bool] = Field(default=False)
|
||||
label: Optional[str]
|
||||
slug: str
|
||||
email: str
|
||||
|
||||
|
||||
class Organization(OrganizationBase, table=True):
|
||||
|
|
@ -26,9 +32,19 @@ class OrganizationWithConfig(BaseModel):
|
|||
config: OrganizationConfig
|
||||
|
||||
|
||||
class OrganizationUpdate(OrganizationBase):
|
||||
pass
|
||||
|
||||
class OrganizationUpdate(SQLModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
about: Optional[str] = None
|
||||
socials: Optional[dict] = None
|
||||
links: Optional[dict] = None
|
||||
logo_image: Optional[str] = None
|
||||
thumbnail_image: Optional[str] = None
|
||||
previews: Optional[dict] = None
|
||||
label: Optional[str] = None
|
||||
slug: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
explore: Optional[bool] = None
|
||||
|
||||
class OrganizationCreate(OrganizationBase):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ from src.services.orgs.orgs import (
|
|||
get_orgs_by_user_admin,
|
||||
update_org,
|
||||
update_org_logo,
|
||||
update_org_preview,
|
||||
update_org_signup_mechanism,
|
||||
update_org_thumbnail,
|
||||
)
|
||||
|
|
@ -334,6 +335,25 @@ async def api_update_org_thumbnail(
|
|||
db_session=db_session,
|
||||
)
|
||||
|
||||
@router.put("/{org_id}/preview")
|
||||
async def api_update_org_preview(
|
||||
request: Request,
|
||||
org_id: str,
|
||||
preview_file: UploadFile,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session: Session = Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Update org thumbnail
|
||||
"""
|
||||
return await update_org_preview(
|
||||
request=request,
|
||||
preview_file=preview_file,
|
||||
org_id=org_id,
|
||||
current_user=current_user,
|
||||
db_session=db_session,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/user/page/{page}/limit/{limit}")
|
||||
async def api_user_orgs(
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ from src.db.organizations import (
|
|||
)
|
||||
from fastapi import HTTPException, UploadFile, status, Request
|
||||
|
||||
from src.services.orgs.uploads import upload_org_logo, upload_org_thumbnail
|
||||
from src.services.orgs.uploads import upload_org_logo, upload_org_preview, upload_org_thumbnail
|
||||
|
||||
|
||||
async def get_organization(
|
||||
|
|
@ -174,7 +174,7 @@ async def create_org(
|
|||
storage=StorageOrgConfig(enabled=True, limit=0),
|
||||
ai=AIOrgConfig(enabled=True, limit=0, model="gpt-4o-mini"),
|
||||
assignments=AssignmentOrgConfig(enabled=True, limit=0),
|
||||
payments=PaymentOrgConfig(enabled=True, stripe_key=""),
|
||||
payments=PaymentOrgConfig(enabled=True),
|
||||
discussions=DiscussionOrgConfig(enabled=True, limit=0),
|
||||
analytics=AnalyticsOrgConfig(enabled=True, limit=0),
|
||||
collaboration=CollaborationOrgConfig(enabled=True, limit=0),
|
||||
|
|
@ -458,6 +458,31 @@ async def update_org_thumbnail(
|
|||
|
||||
return {"detail": "Thumbnail updated"}
|
||||
|
||||
async def update_org_preview(
|
||||
request: Request,
|
||||
preview_file: UploadFile,
|
||||
org_id: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
statement = select(Organization).where(Organization.id == org_id)
|
||||
result = db_session.exec(statement)
|
||||
|
||||
org = result.first()
|
||||
|
||||
if not org:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Organization not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, org.org_uuid, current_user, "update", db_session)
|
||||
|
||||
# Upload logo
|
||||
name_in_disk = await upload_org_preview(preview_file, org.org_uuid)
|
||||
|
||||
return {"name_in_disk": name_in_disk}
|
||||
|
||||
async def delete_org(
|
||||
request: Request,
|
||||
|
|
@ -675,6 +700,19 @@ async def get_org_join_mechanism(
|
|||
|
||||
return signup_mechanism
|
||||
|
||||
async def upload_org_preview_service(
|
||||
preview_file: UploadFile,
|
||||
org_uuid: str,
|
||||
) -> dict:
|
||||
# No need for request or current_user since we're not doing RBAC checks for previews
|
||||
|
||||
# Upload preview
|
||||
name_in_disk = await upload_org_preview(preview_file, org_uuid)
|
||||
|
||||
return {
|
||||
"detail": "Preview uploaded successfully",
|
||||
"filename": name_in_disk
|
||||
}
|
||||
|
||||
## 🔒 RBAC Utils ##
|
||||
|
||||
|
|
|
|||
|
|
@ -31,3 +31,18 @@ async def upload_org_thumbnail(thumbnail_file, org_uuid):
|
|||
)
|
||||
|
||||
return name_in_disk
|
||||
|
||||
|
||||
async def upload_org_preview(file, org_uuid: str) -> str:
|
||||
contents = file.file.read()
|
||||
name_in_disk = f"{uuid4()}.{file.filename.split('.')[-1]}"
|
||||
|
||||
await upload_content(
|
||||
"previews",
|
||||
"orgs",
|
||||
org_uuid,
|
||||
contents,
|
||||
name_in_disk,
|
||||
)
|
||||
|
||||
return name_in_disk
|
||||
Loading…
Add table
Add a link
Reference in a new issue