feat: support external repositories for git mounts

- Make project_id nullable in git_repositories table (migration)
- Allow external repos not tied to any project
- Update validation to allow user-owned external repos in git mounts
- Add /projects/repositories endpoint to list all user repos
- Update frontend to fetch all user repos for git mount selector
- TypeScript and build pass
This commit is contained in:
Alex Blank
2026-05-27 10:28:31 +02:00
parent e7adfb462b
commit 93b415c53e
7 changed files with 85 additions and 15 deletions
+9 -2
View File
@@ -275,8 +275,10 @@ async def _validate_git_mounts(
Repositories must:
1. Exist
2. Belong to the user
3. If project_id is specified, belong to that project
2. Belong to the user (external repos with no project are allowed)
3. If project_id is specified, repos can be either:
- External repos (project_id is null) belonging to the user
- Project repos belonging to that project
"""
for mount in git_mounts:
repo_id = mount.get("repo_id")
@@ -307,6 +309,11 @@ async def _validate_git_mounts(
detail=f"Not authorized to access repository: {repo_id}",
)
# External repos (no project) are always allowed for git mounts
if repo.project_id is None:
continue
# Project repos are allowed if they belong to the profile's project
if project_id is not None and repo.project_id != project_id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
+25
View File
@@ -262,6 +262,31 @@ async def list_repositories(
return list(result.scalars().all())
@router.get(
"/repositories",
response_model=list[GitRepositoryResponse],
summary="List all user repositories",
description="List all git repositories owned by the user, including external repositories not tied to any project.",
)
async def list_user_repositories(
user_id: uuid.UUID = Depends(get_current_user_id),
session: AsyncSession = Depends(get_db_session),
) -> list[GitRepository]:
"""List all repositories owned by the user.
Args:
user_id: ID of the authenticated user.
session: Database session.
Returns:
List of all repositories owned by the user.
"""
result = await session.execute(
select(GitRepository).where(GitRepository.owner_id == user_id)
)
return list(result.scalars().all())
@router.delete(
"/{project_id}/repositories/{repo_id}",
status_code=status.HTTP_204_NO_CONTENT,
+1 -1
View File
@@ -19,7 +19,7 @@ class GitRepository(UUIDPrimaryKeyMixin, TimestampMixin, Base):
name: Mapped[str] = mapped_column(String(255))
path: Mapped[str] = mapped_column(String(1024))
project_id: Mapped[uuid.UUID] = mapped_column(UUID(), ForeignKey("projects.id"), nullable=False)
project_id: Mapped[uuid.UUID | None] = mapped_column(UUID(), ForeignKey("projects.id"), nullable=True)
owner_id: Mapped[uuid.UUID] = mapped_column(UUID(), ForeignKey("users.id"), nullable=False)
is_mirror: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
remote_url: Mapped[str | None] = mapped_column(String(1024), nullable=True)