storage.mjs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import { Octokit, App } from 'octokit'
  2. export default {
  3. async activated () { },
  4. async deactivated () { },
  5. async init () { },
  6. /**
  7. * SETUP FUNCTIONS
  8. */
  9. async setup (id, state) {
  10. try {
  11. switch (state.step) {
  12. // --------------------------------------------
  13. // -> VALIDATE CALLBACK CODE AFTER APP CREATION
  14. // --------------------------------------------
  15. case 'connect': {
  16. const gh = new Octokit({
  17. userAgent: 'wikijs'
  18. })
  19. const resp = await gh.request('POST /app-manifests/{code}/conversions', {
  20. code: state.code
  21. })
  22. if (resp.status > 200 && resp.status < 300) {
  23. await WIKI.db.storage.query().patch({
  24. config: {
  25. appId: resp.data.id,
  26. appName: resp.data.name,
  27. appSlug: resp.data.slug,
  28. appClientId: resp.data.client_id,
  29. appClientSecret: resp.data.client_secret,
  30. appWebhookSecret: resp.data.webhook_secret,
  31. appPem: resp.data.pem,
  32. appPermissions: resp.data.permissions,
  33. appEvents: resp.data.events,
  34. ownerLogin: resp.data.owner?.login,
  35. ownerId: resp.data.owner?.id
  36. },
  37. state: {
  38. current: 'ok',
  39. setup: 'pendinginstall'
  40. }
  41. }).where('id', id)
  42. return {
  43. nextStep: 'installApp',
  44. url: `https://github.com/apps/${resp.data.slug}/installations/new/permissions?target_id=${resp.data.owner?.id}`
  45. }
  46. } else {
  47. throw new Error('GitHub refused the code or could not be reached.')
  48. }
  49. }
  50. // -----------------------
  51. // VERIFY APP INSTALLATION
  52. // -----------------------
  53. case 'verify': {
  54. const tgt = await WIKI.db.storage.query().findById(id)
  55. if (!tgt) {
  56. throw new Error('Invalid Target ID')
  57. }
  58. const ghApp = new App({
  59. appId: tgt.config.appId,
  60. privateKey: tgt.config.appPem,
  61. Octokit: Octokit.defaults({
  62. userAgent: 'wikijs'
  63. }),
  64. oauth: {
  65. clientId: tgt.config.appClientId,
  66. clientSecret: tgt.config.appClientSecret
  67. },
  68. webhooks: {
  69. secret: tgt.config.appWebhookSecret
  70. }
  71. })
  72. // -> Find Installation ID
  73. let installId = null
  74. let installTotal = 0
  75. for await (const { installation } of ghApp.eachInstallation.iterator()) {
  76. if (installTotal < 1) {
  77. installId = installation.id
  78. WIKI.logger.debug(`Using GitHub App installation ID ${installId}`)
  79. }
  80. installTotal++
  81. }
  82. if (installTotal < 1) {
  83. throw new Error('App is not installed on any GitHub account!')
  84. } else if (installTotal > 1) {
  85. WIKI.logger.warn(`GitHub App ${tgt.config.appName} is installed on more than 1 account. Only the first one ${installId} will be used.`)
  86. }
  87. // -> Fetch Repository Info
  88. let repo = null
  89. let repoTotal = 0
  90. for await (const { repository } of ghApp.eachRepository.iterator({ installationId: installId })) {
  91. if (repository.archived || repository.disabled) {
  92. WIKI.logger.debug(`Skipping GitHub Repository ${repo.id} because of it is archived or disabled.`)
  93. continue
  94. }
  95. if (repoTotal < 1) {
  96. repo = repository
  97. WIKI.logger.debug(`Using GitHub Repository ${repo.id}`)
  98. }
  99. repoTotal++
  100. }
  101. if (repoTotal < 1) {
  102. throw new Error('App is not installed on any GitHub repository!')
  103. } else if (repoTotal > 1) {
  104. WIKI.logger.warn(`GitHub App ${tgt.config.appName} is installed on more than 1 repository. Only the first one (${repo.full_name}) will be used.`)
  105. }
  106. // -> Save install/repo info
  107. await WIKI.db.storage.query().patch({
  108. isEnabled: true,
  109. config: {
  110. ...tgt.config,
  111. installId,
  112. repoId: repo.id,
  113. repoName: repo.name,
  114. repoOwner: repo.owner?.login,
  115. repoDefaultBranch: repo.default_branch,
  116. repoFullName: repo.full_name
  117. },
  118. state: {
  119. current: 'ok',
  120. setup: 'configured'
  121. }
  122. }).where('id', id)
  123. return {
  124. nextStep: 'completed'
  125. }
  126. }
  127. default: {
  128. throw new Error('Invalid Setup Step')
  129. }
  130. }
  131. } catch (err) {
  132. WIKI.logger.warn('GitHub Storage Module Setup Failed:')
  133. WIKI.logger.warn(err)
  134. throw err
  135. }
  136. },
  137. async setupDestroy (id) {
  138. try {
  139. const tgt = await WIKI.db.storage.query().findById(id)
  140. if (!tgt) {
  141. throw new Error('Invalid Target ID')
  142. }
  143. WIKI.logger.info('Resetting GitHub storage configuration...')
  144. const ghApp = new App({
  145. appId: tgt.config.appId,
  146. privateKey: tgt.config.appPem,
  147. Octokit: Octokit.defaults({
  148. userAgent: 'wikijs'
  149. }),
  150. oauth: {
  151. clientId: tgt.config.appClientId,
  152. clientSecret: tgt.config.appClientSecret
  153. },
  154. webhooks: {
  155. secret: tgt.config.appWebhookSecret
  156. }
  157. })
  158. // -> Reset storage module config
  159. await WIKI.db.storage.query().patch({
  160. isEnabled: false,
  161. config: {},
  162. state: {
  163. current: 'ok',
  164. setup: 'notconfigured'
  165. }
  166. }).where('id', id)
  167. // -> Try to delete installation on GitHub
  168. if (tgt.config.installId) {
  169. try {
  170. await ghApp.octokit.request('DELETE /app/installations/{installation_id}', {
  171. installation_id: tgt.config.installId
  172. })
  173. WIKI.logger.info('Deleted GitHub installation successfully.')
  174. } catch (err) {
  175. WIKI.logger.warn('Could not delete GitHub installation automatically. Please remove the installation on GitHub.')
  176. }
  177. }
  178. } catch (err) {
  179. WIKI.logger.warn('GitHub Storage Module Destroy Failed:')
  180. WIKI.logger.warn(err)
  181. throw err
  182. }
  183. },
  184. async created (page) { },
  185. async updated (page) { },
  186. async deleted (page) { },
  187. async renamed (page) { },
  188. async assetUploaded (asset) { },
  189. async assetDeleted (asset) { },
  190. async assetRenamed (asset) { },
  191. async getLocalLocation () { },
  192. async exportAll () { }
  193. }