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.

389 lines
13 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. const _ = require('lodash')
  2. const Promise = require('bluebird')
  3. const getos = Promise.promisify(require('getos'))
  4. const os = require('os')
  5. const filesize = require('filesize')
  6. const path = require('path')
  7. const fs = require('fs-extra')
  8. const moment = require('moment')
  9. const graphHelper = require('../../helpers/graph')
  10. const request = require('request-promise')
  11. const crypto = require('crypto')
  12. const nanoid = require('nanoid/non-secure').customAlphabet('1234567890abcdef', 10)
  13. /* global WIKI */
  14. const dbTypes = {
  15. mysql: 'MySQL',
  16. mariadb: 'MariaDB',
  17. postgres: 'PostgreSQL',
  18. sqlite: 'SQLite',
  19. mssql: 'MS SQL Server'
  20. }
  21. module.exports = {
  22. Query: {
  23. async system () { return {} }
  24. },
  25. Mutation: {
  26. async system () { return {} }
  27. },
  28. SystemQuery: {
  29. flags () {
  30. return _.transform(WIKI.config.flags, (result, value, key) => {
  31. result.push({ key, value })
  32. }, [])
  33. },
  34. async info () { return {} },
  35. async extensions () {
  36. const exts = Object.values(WIKI.extensions.ext).map(ext => _.pick(ext, ['key', 'title', 'description', 'isInstalled']))
  37. for (let ext of exts) {
  38. ext.isCompatible = await WIKI.extensions.ext[ext.key].isCompatible()
  39. }
  40. return exts
  41. }
  42. },
  43. SystemMutation: {
  44. async updateFlags (obj, args, context) {
  45. WIKI.config.flags = _.transform(args.flags, (result, row) => {
  46. _.set(result, row.key, row.value)
  47. }, {})
  48. await WIKI.configSvc.applyFlags()
  49. await WIKI.configSvc.saveToDb(['flags'])
  50. return {
  51. responseResult: graphHelper.generateSuccess('System Flags applied successfully')
  52. }
  53. },
  54. async resetTelemetryClientId (obj, args, context) {
  55. try {
  56. WIKI.telemetry.generateClientId()
  57. await WIKI.configSvc.saveToDb(['telemetry'])
  58. return {
  59. responseResult: graphHelper.generateSuccess('Telemetry state updated successfully')
  60. }
  61. } catch (err) {
  62. return graphHelper.generateError(err)
  63. }
  64. },
  65. async setTelemetry (obj, args, context) {
  66. try {
  67. _.set(WIKI.config, 'telemetry.isEnabled', args.enabled)
  68. WIKI.telemetry.enabled = args.enabled
  69. await WIKI.configSvc.saveToDb(['telemetry'])
  70. return {
  71. responseResult: graphHelper.generateSuccess('Telemetry Client ID has been reset successfully')
  72. }
  73. } catch (err) {
  74. return graphHelper.generateError(err)
  75. }
  76. },
  77. async performUpgrade (obj, args, context) {
  78. try {
  79. if (process.env.UPGRADE_COMPANION) {
  80. await request({
  81. method: 'POST',
  82. uri: 'http://wiki-update-companion/upgrade'
  83. })
  84. return {
  85. responseResult: graphHelper.generateSuccess('Upgrade has started.')
  86. }
  87. } else {
  88. throw new Error('You must run the wiki-update-companion container and pass the UPGRADE_COMPANION env var in order to use this feature.')
  89. }
  90. } catch (err) {
  91. return graphHelper.generateError(err)
  92. }
  93. },
  94. /**
  95. * Import Users from a v1 installation
  96. */
  97. async importUsersFromV1(obj, args, context) {
  98. try {
  99. const MongoClient = require('mongodb').MongoClient
  100. if (args.mongoDbConnString && args.mongoDbConnString.length > 10) {
  101. // -> Connect to DB
  102. const client = await MongoClient.connect(args.mongoDbConnString, {
  103. appname: `Wiki.js ${WIKI.version} Migration Tool`
  104. })
  105. const dbUsers = client.db().collection('users')
  106. const userCursor = dbUsers.find({ email: { '$ne': 'guest' } })
  107. const curDateISO = new Date().toISOString()
  108. let failed = []
  109. let usersCount = 0
  110. let groupsCount = 0
  111. let assignableGroups = []
  112. let reuseGroups = []
  113. // -> Create SINGLE group
  114. if (args.groupMode === `SINGLE`) {
  115. const singleGroup = await WIKI.models.groups.query().insert({
  116. name: `Import_${curDateISO}`,
  117. permissions: JSON.stringify(WIKI.data.groups.defaultPermissions),
  118. pageRules: JSON.stringify(WIKI.data.groups.defaultPageRules)
  119. })
  120. groupsCount++
  121. assignableGroups.push(singleGroup.id)
  122. }
  123. // -> Iterate all users
  124. while (await userCursor.hasNext()) {
  125. const usr = await userCursor.next()
  126. let usrGroup = []
  127. if (args.groupMode === `MULTI`) {
  128. // -> Check if global admin
  129. if (_.some(usr.rights, ['role', 'admin'])) {
  130. usrGroup.push(1)
  131. } else {
  132. // -> Check if identical group already exists
  133. const currentRights = _.sortBy(_.map(usr.rights, r => _.pick(r, ['role', 'path', 'exact', 'deny'])), ['role', 'path', 'exact', 'deny'])
  134. const ruleSetId = crypto.createHash('sha1').update(JSON.stringify(currentRights)).digest('base64')
  135. const existingGroup = _.find(reuseGroups, ['hash', ruleSetId])
  136. if (existingGroup) {
  137. usrGroup.push(existingGroup.groupId)
  138. } else {
  139. // -> Build new group
  140. const pageRules = _.map(usr.rights, r => {
  141. let roles = ['read:pages', 'read:assets', 'read:comments', 'write:comments']
  142. if (r.role === `write`) {
  143. roles = _.concat(roles, ['write:pages', 'manage:pages', 'read:source', 'read:history', 'write:assets', 'manage:assets'])
  144. }
  145. return {
  146. id: nanoid(),
  147. roles: roles,
  148. match: r.exact ? 'EXACT' : 'START',
  149. deny: r.deny,
  150. path: (r.path.indexOf('/') === 0) ? r.path.substring(1) : r.path,
  151. locales: []
  152. }
  153. })
  154. const perms = _.chain(pageRules).reject('deny').map('roles').union().flatten().value()
  155. // -> Create new group
  156. const newGroup = await WIKI.models.groups.query().insert({
  157. name: `Import_${curDateISO}_${groupsCount + 1}`,
  158. permissions: JSON.stringify(perms),
  159. pageRules: JSON.stringify(pageRules)
  160. })
  161. reuseGroups.push({
  162. groupId: newGroup.id,
  163. hash: ruleSetId
  164. })
  165. groupsCount++
  166. usrGroup.push(newGroup.id)
  167. }
  168. }
  169. }
  170. // -> Create User
  171. try {
  172. await WIKI.models.users.createNewUser({
  173. providerKey: usr.provider,
  174. email: usr.email,
  175. name: usr.name,
  176. passwordRaw: usr.password,
  177. groups: (usrGroup.length > 0) ? usrGroup : assignableGroups,
  178. mustChangePassword: false,
  179. sendWelcomeEmail: false
  180. })
  181. usersCount++
  182. } catch (err) {
  183. failed.push({
  184. provider: usr.provider,
  185. email: usr.email,
  186. error: err.message
  187. })
  188. WIKI.logger.warn(`${usr.email}: ${err}`)
  189. }
  190. }
  191. // -> Reload group permissions
  192. if (args.groupMode !== `NONE`) {
  193. await WIKI.auth.reloadGroups()
  194. WIKI.events.outbound.emit('reloadGroups')
  195. }
  196. client.close()
  197. return {
  198. responseResult: graphHelper.generateSuccess('Import completed.'),
  199. usersCount: usersCount,
  200. groupsCount: groupsCount,
  201. failed: failed
  202. }
  203. } else {
  204. throw new Error('MongoDB Connection String is missing or invalid.')
  205. }
  206. } catch (err) {
  207. return graphHelper.generateError(err)
  208. }
  209. },
  210. /**
  211. * Set HTTPS Redirection State
  212. */
  213. async setHTTPSRedirection (obj, args, context) {
  214. _.set(WIKI.config, 'server.sslRedir', args.enabled)
  215. await WIKI.configSvc.saveToDb(['server'])
  216. return {
  217. responseResult: graphHelper.generateSuccess('HTTP Redirection state set successfully.')
  218. }
  219. },
  220. /**
  221. * Renew SSL Certificate
  222. */
  223. async renewHTTPSCertificate (obj, args, context) {
  224. try {
  225. if (!WIKI.config.ssl.enabled) {
  226. throw new WIKI.Error.SystemSSLDisabled()
  227. } else if (WIKI.config.ssl.provider !== `letsencrypt`) {
  228. throw new WIKI.Error.SystemSSLRenewInvalidProvider()
  229. } else if (!WIKI.servers.le) {
  230. throw new WIKI.Error.SystemSSLLEUnavailable()
  231. } else {
  232. await WIKI.servers.le.requestCertificate()
  233. await WIKI.servers.restartServer('https')
  234. return {
  235. responseResult: graphHelper.generateSuccess('SSL Certificate renewed successfully.')
  236. }
  237. }
  238. } catch (err) {
  239. return graphHelper.generateError(err)
  240. }
  241. }
  242. },
  243. SystemInfo: {
  244. configFile () {
  245. return path.join(process.cwd(), 'config.yml')
  246. },
  247. cpuCores () {
  248. return os.cpus().length
  249. },
  250. currentVersion () {
  251. return WIKI.version
  252. },
  253. dbType () {
  254. return _.get(dbTypes, WIKI.config.db.type, 'Unknown DB')
  255. },
  256. async dbVersion () {
  257. let version = 'Unknown Version'
  258. switch (WIKI.config.db.type) {
  259. case 'mariadb':
  260. case 'mysql':
  261. const resultMYSQL = await WIKI.models.knex.raw('SELECT VERSION() as version;')
  262. version = _.get(resultMYSQL, '[0][0].version', 'Unknown Version')
  263. break
  264. case 'mssql':
  265. const resultMSSQL = await WIKI.models.knex.raw('SELECT @@VERSION as version;')
  266. version = _.get(resultMSSQL, '[0].version', 'Unknown Version')
  267. break
  268. case 'postgres':
  269. version = _.get(WIKI.models, 'knex.client.version', 'Unknown Version')
  270. break
  271. case 'sqlite':
  272. version = _.get(WIKI.models, 'knex.client.driver.VERSION', 'Unknown Version')
  273. break
  274. }
  275. return version
  276. },
  277. dbHost () {
  278. if (WIKI.config.db.type === 'sqlite') {
  279. return WIKI.config.db.storage
  280. } else {
  281. return WIKI.config.db.host
  282. }
  283. },
  284. hostname () {
  285. return os.hostname()
  286. },
  287. httpPort () {
  288. return WIKI.servers.servers.http ? _.get(WIKI.servers.servers.http.address(), 'port', 0) : 0
  289. },
  290. httpRedirection () {
  291. return _.get(WIKI.config, 'server.sslRedir', false)
  292. },
  293. httpsPort () {
  294. return WIKI.servers.servers.https ? _.get(WIKI.servers.servers.https.address(), 'port', 0) : 0
  295. },
  296. latestVersion () {
  297. return WIKI.system.updates.version
  298. },
  299. latestVersionReleaseDate () {
  300. return moment.utc(WIKI.system.updates.releaseDate)
  301. },
  302. nodeVersion () {
  303. return process.version.substr(1)
  304. },
  305. async operatingSystem () {
  306. let osLabel = `${os.type()} (${os.platform()}) ${os.release()} ${os.arch()}`
  307. if (os.platform() === 'linux') {
  308. const osInfo = await getos()
  309. osLabel = `${os.type()} - ${osInfo.dist} (${osInfo.codename || os.platform()}) ${osInfo.release || os.release()} ${os.arch()}`
  310. }
  311. return osLabel
  312. },
  313. async platform () {
  314. const isDockerized = await fs.pathExists('/.dockerenv')
  315. if (isDockerized) {
  316. return 'docker'
  317. }
  318. return os.platform()
  319. },
  320. ramTotal () {
  321. return filesize(os.totalmem())
  322. },
  323. sslDomain () {
  324. return WIKI.config.ssl.enabled && WIKI.config.ssl.provider === `letsencrypt` ? WIKI.config.ssl.domain : null
  325. },
  326. sslExpirationDate () {
  327. return WIKI.config.ssl.enabled && WIKI.config.ssl.provider === `letsencrypt` ? _.get(WIKI.config.letsencrypt, 'payload.expires', null) : null
  328. },
  329. sslProvider () {
  330. return WIKI.config.ssl.enabled ? WIKI.config.ssl.provider : null
  331. },
  332. sslStatus () {
  333. return 'OK'
  334. },
  335. sslSubscriberEmail () {
  336. return WIKI.config.ssl.enabled && WIKI.config.ssl.provider === `letsencrypt` ? WIKI.config.ssl.subscriberEmail : null
  337. },
  338. telemetry () {
  339. return WIKI.telemetry.enabled
  340. },
  341. telemetryClientId () {
  342. return WIKI.config.telemetry.clientId
  343. },
  344. async upgradeCapable () {
  345. return !_.isNil(process.env.UPGRADE_COMPANION)
  346. },
  347. workingDirectory () {
  348. return process.cwd()
  349. },
  350. async groupsTotal () {
  351. const total = await WIKI.models.groups.query().count('* as total').first()
  352. return _.toSafeInteger(total.total)
  353. },
  354. async pagesTotal () {
  355. const total = await WIKI.models.pages.query().count('* as total').first()
  356. return _.toSafeInteger(total.total)
  357. },
  358. async usersTotal () {
  359. const total = await WIKI.models.users.query().count('* as total').first()
  360. return _.toSafeInteger(total.total)
  361. },
  362. async tagsTotal () {
  363. const total = await WIKI.models.tags.query().count('* as total').first()
  364. return _.toSafeInteger(total.total)
  365. }
  366. }
  367. }