diff --git a/packages/das/src/api/admin.controller.ts b/packages/das/src/api/admin.controller.ts index 2999c2f..05c4f74 100644 --- a/packages/das/src/api/admin.controller.ts +++ b/packages/das/src/api/admin.controller.ts @@ -2,6 +2,7 @@ import { BadRequestException, Body, Controller, + Get, NotFoundException, Post, UseGuards, @@ -149,6 +150,74 @@ export class AdminController { }; } + @Post("repos/deregister") + @ApiOperation({ + summary: "Flip a repo to unregistered", + description: + "Sets registered=false on a repo. Keeps the row, its data, and " + + "installation_id intact so the repo can be re-registered later.", + }) + @ApiBody({ + schema: { + type: "object", + required: ["repoFullName"], + properties: { + repoFullName: { type: "string", example: "entrius/gittensor-ui" }, + }, + }, + }) + async deregisterRepo(@Body() body: RegisterBody): Promise<{ + repoFullName: string; + registered: false; + }> { + const repoFullName = validateRepoFullName(body?.repoFullName); + + const repo = await this.repoRepo + .createQueryBuilder("repo") + .select(["repo.repoFullName"]) + .where("LOWER(repo.repo_full_name) = LOWER(:repoFullName)", { + repoFullName, + }) + .getOne(); + + if (!repo) { + throw new NotFoundException(`Repo ${repoFullName} not found`); + } + + await this.repoRepo.update( + { repoFullName: repo.repoFullName }, + { registered: false }, + ); + + return { repoFullName: repo.repoFullName, registered: false }; + } + + @Get("repos") + @ApiOperation({ + summary: "List all repo rows", + description: + "Returns every repo row with its registration state and whether a " + + "GitHub App installation is attached. Sorted by repoFullName.", + }) + async listRepos(): Promise< + Array<{ + repoFullName: string; + registered: boolean; + hasInstallation: boolean; + }> + > { + const repos = await this.repoRepo.find({ + select: ["repoFullName", "registered", "installationId"], + order: { repoFullName: "ASC" }, + }); + + return repos.map((repo) => ({ + repoFullName: repo.repoFullName, + registered: repo.registered, + hasInstallation: repo.installationId !== null, + })); + } + /** Resolve the canonical repos PK after a case-insensitive match. */ private async resolveInstalledRepoFullName( repoFullName: string,