You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
3.5 KiB

  1. import * as path from 'path';
  2. import {Uuid} from '@theoryofnekomata/uuid-buffer';
  3. import {PrismaClient} from '@prisma/client';
  4. import {spawn} from '../../utils/process';
  5. import {mkdirp, unlink} from '../../utils/fs';
  6. import {User} from '../common';
  7. import {LogService, LogServiceImpl} from '../log';
  8. import * as models from './models';
  9. import {notFoundFactory} from '../../utils/error';
  10. export interface GitService {
  11. create(data: models.CreateRepoData, subject: User): Promise<models.Repo>
  12. delete(repoId: models.Repo['id'], subject: User): Promise<void>
  13. }
  14. export class GitServiceImpl implements GitService {
  15. private readonly prismaClient: PrismaClient
  16. private readonly logService: LogService
  17. constructor() {
  18. this.prismaClient = new PrismaClient();
  19. this.logService = new LogServiceImpl()
  20. }
  21. private static getRepoBasePath(repoIdString: string) {
  22. return path.join('repos', repoIdString)
  23. }
  24. private async getOwnerName(ownerId: Uuid, ownerType: models.OwnerType) {
  25. let owner: models.Owner
  26. if (ownerType === models.OwnerType.USER) {
  27. owner = await this.prismaClient.user.findUnique({
  28. where: {
  29. id: ownerId,
  30. },
  31. rejectOnNotFound: notFoundFactory(models.UserNotFoundError),
  32. })
  33. return owner.username;
  34. }
  35. if (ownerType === models.OwnerType.ORG) {
  36. owner = await this.prismaClient.org.findUnique({
  37. where: {
  38. id: ownerId,
  39. },
  40. rejectOnNotFound: notFoundFactory(models.OrgNotFoundError),
  41. })
  42. return owner.name;
  43. }
  44. throw new models.UnknownOwnerTypeError('Unknown owner type.')
  45. }
  46. private static async createRepoFiles(repoIdString: string) {
  47. const repoBasePath = GitServiceImpl.getRepoBasePath(repoIdString);
  48. await mkdirp(repoBasePath);
  49. await spawn(repoBasePath, 'git', ['init', '--bare']);
  50. }
  51. async create(data: models.CreateRepoData, subject: User): Promise<models.Repo> {
  52. const repoId = Uuid.v4();
  53. const repoIdString = repoId.toString()
  54. await GitServiceImpl.createRepoFiles(repoIdString);
  55. const repoMetadata = await this.prismaClient.repo.create({
  56. data: {
  57. id: repoId,
  58. name: data.name,
  59. visibility: data.visibility,
  60. ownerId: data.ownerId,
  61. ownerType: data.ownerType,
  62. },
  63. });
  64. const ownerName = await this.getOwnerName(data.ownerId, data.ownerType);
  65. await this.logService.create(
  66. subject,
  67. models.GitAction.REPO_CREATED,
  68. {
  69. key: 'repoId',
  70. value: repoIdString,
  71. },
  72. {
  73. key: 'repoName',
  74. value: data.name,
  75. },
  76. {
  77. key: 'ownerId',
  78. value: data.ownerId.toString(),
  79. },
  80. {
  81. key: 'ownerName',
  82. value: ownerName,
  83. },
  84. );
  85. return {
  86. ...repoMetadata,
  87. id: Uuid.from(repoMetadata.id),
  88. };
  89. }
  90. async delete(repoId: models.Repo['id'], subject: User): Promise<void> {
  91. const repoIdString = repoId.toString();
  92. const repo = await this.prismaClient.repo.findUnique({
  93. where: {
  94. id: repoId,
  95. },
  96. rejectOnNotFound: notFoundFactory(models.RepoNotFoundError),
  97. });
  98. await this.prismaClient.repo.delete({
  99. where: {
  100. id: repoId,
  101. },
  102. });
  103. const repoBasePath = GitServiceImpl.getRepoBasePath(repoIdString);
  104. await unlink(repoBasePath);
  105. await this.logService.create(
  106. subject,
  107. models.GitAction.REPO_REMOVED,
  108. {
  109. key: 'repoId',
  110. value: repoIdString,
  111. },
  112. {
  113. key: 'repoName',
  114. value: repo.name,
  115. },
  116. );
  117. }
  118. }