storage.js 6.6 KB

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