import * as path from 'path'; import {Uuid} from '@theoryofnekomata/uuid-buffer'; import {PrismaClient} from '@prisma/client'; import {spawn} from '../../utils/process'; import {mkdirp, unlink} from '../../utils/fs'; import {User} from '../common'; import {LogService, LogServiceImpl} from '../log'; import * as models from './models'; import {notFoundFactory} from '../../utils/error'; export interface GitService { create(data: models.CreateRepoData, subject: User): Promise delete(repoId: models.Repo['id'], subject: User): Promise } export class GitServiceImpl implements GitService { private readonly prismaClient: PrismaClient private readonly logService: LogService constructor() { this.prismaClient = new PrismaClient(); this.logService = new LogServiceImpl() } private static getRepoBasePath(repoIdString: string) { return path.join('repos', repoIdString) } private async getOwnerName(ownerId: Uuid, ownerType: models.OwnerType) { let owner: models.Owner if (ownerType === models.OwnerType.USER) { owner = await this.prismaClient.user.findUnique({ where: { id: ownerId, }, rejectOnNotFound: notFoundFactory(models.UserNotFoundError), }) return owner.username; } if (ownerType === models.OwnerType.ORG) { owner = await this.prismaClient.org.findUnique({ where: { id: ownerId, }, rejectOnNotFound: notFoundFactory(models.OrgNotFoundError), }) return owner.name; } throw new models.UnknownOwnerTypeError('Unknown owner type.') } private static async createRepoFiles(repoIdString: string) { const repoBasePath = GitServiceImpl.getRepoBasePath(repoIdString); await mkdirp(repoBasePath); await spawn(repoBasePath, 'git', ['init', '--bare']); } async create(data: models.CreateRepoData, subject: User): Promise { const repoId = Uuid.v4(); const repoIdString = repoId.toString() await GitServiceImpl.createRepoFiles(repoIdString); const repoMetadata = await this.prismaClient.repo.create({ data: { id: repoId, name: data.name, visibility: data.visibility, ownerId: data.ownerId, ownerType: data.ownerType, }, }); const ownerName = await this.getOwnerName(data.ownerId, data.ownerType); await this.logService.create( subject, models.GitAction.REPO_CREATED, { key: 'repoId', value: repoIdString, }, { key: 'repoName', value: data.name, }, { key: 'ownerId', value: data.ownerId.toString(), }, { key: 'ownerName', value: ownerName, }, ); return { ...repoMetadata, id: Uuid.from(repoMetadata.id), }; } async delete(repoId: models.Repo['id'], subject: User): Promise { const repoIdString = repoId.toString(); const repo = await this.prismaClient.repo.findUnique({ where: { id: repoId, }, rejectOnNotFound: notFoundFactory(models.RepoNotFoundError), }); await this.prismaClient.repo.delete({ where: { id: repoId, }, }); const repoBasePath = GitServiceImpl.getRepoBasePath(repoIdString); await unlink(repoBasePath); await this.logService.create( subject, models.GitAction.REPO_REMOVED, { key: 'repoId', value: repoIdString, }, { key: 'repoName', value: repo.name, }, ); } }