feat: Init explore metadata features & redesign org settings panel

This commit is contained in:
swve 2024-12-18 00:24:37 +01:00
parent 87787724c4
commit bfd27ef6e3
12 changed files with 1419 additions and 217 deletions

View file

@ -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 ###

View file

@ -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

View file

@ -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(

View file

@ -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 ##

View file

@ -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