diff --git a/docs/API.md b/docs/API.md index 7d4e03f..9f121f7 100644 --- a/docs/API.md +++ b/docs/API.md @@ -125,6 +125,24 @@ When several sources are connected, an optional `sourceId` picks which one the o `PATCH /api/organizations/:id` with `{ "sourceId": "…" }` re-pins an existing organization to another source, and `{ "sourceId": null }` clears the pin so the organization follows every source's repositories of the same name again. A pin whose source was removed falls back the same way. +### Export as CSV + +`GET /api/repositories/export` and `GET /api/organizations/export` return everything the account tracks as a CSV file, so the list can be shared with people who do not have a login here (issue #428). + +Both answer `text/csv; charset=utf-8` with a download name such as `gitea-mirror-repositories-2026-09-22.csv`, start with a header row, use CRLF line endings and RFC 4180 quoting, and write dates as ISO strings and booleans as `true` or `false`. Repositories are ordered by full name, organizations by name. The export has no filters: it is every row of the account. Internal fields are left out, that is the ids, the metadata sync state and the mirror option overrides. + +Repository columns: `name`, `fullName`, `url`, `cloneUrl`, `owner`, `organization`, `sourceProvider`, `sourceUrl`, `destinationProvider`, `destinationUrl`, `destinationOrg`, `mirroredLocation`, `visibility`, `isPrivate`, `isForked`, `forkedFrom`, `isStarred`, `isArchived`, `hasLFS`, `hasSubmodules`, `hasIssues`, `language`, `description`, `defaultBranch`, `size`, `status`, `lastMirrored`, `errorMessage`, `importedAt`, `createdAt`, `updatedAt`. + +Organization columns: `name`, `membershipRole`, `isIncluded`, `destinationOrg`, `status`, `repositoryCount`, `publicRepositoryCount`, `privateRepositoryCount`, `forkRepositoryCount`, `lastMirrored`, `errorMessage`, `createdAt`, `updatedAt`. + +```bash +curl -sS "https://mirror.example.com/api/repositories/export" \ + -H "x-api-key: gm_..." \ + -o repositories.csv +``` + +The **Export CSV** button on the Repositories and Organizations pages downloads the same files. + ### Reconcile the destination `POST /api/cleanup/reconcile` diff --git a/src/components/organizations/Organization.tsx b/src/components/organizations/Organization.tsx index 705be34..94a4336 100644 --- a/src/components/organizations/Organization.tsx +++ b/src/components/organizations/Organization.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; -import { Search, RefreshCw, FlipHorizontal, Filter, LoaderCircle, Trash2 } from "lucide-react"; +import { Search, RefreshCw, FlipHorizontal, Filter, LoaderCircle, Trash2, Download } from "lucide-react"; import type { MirrorJob, Organization } from "@/lib/db/schema"; import { OrganizationList } from "./OrganizationsList"; import AddOrganizationDialog from "./AddOrganizationDialog"; @@ -29,6 +29,7 @@ import { toast } from "sonner"; import { invalidateConfigCache, useConfigStatus } from "@/hooks/useConfigStatus"; import { useNavigation } from "@/components/layout/MainLayout"; import { useLiveRefresh } from "@/hooks/useLiveRefresh"; +import { withBase } from "@/lib/base-path"; import { Drawer, DrawerClose, @@ -568,7 +569,7 @@ export function Organization() {
{/* Mobile: Search bar with filter button */}
-
+
- + + + + {/* CSV export of every tracked organization */} + + - + + + + {/* CSV export of every tracked repository */} + + {/* Mirror All action */}