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.

321 lines
10 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/generate')
  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. },
  36. SystemMutation: {
  37. async updateFlags (obj, args, context) {
  38. WIKI.config.flags = _.transform(args.flags, (result, row) => {
  39. _.set(result, row.key, row.value)
  40. }, {})
  41. await WIKI.configSvc.applyFlags()
  42. await WIKI.configSvc.saveToDb(['flags'])
  43. return {
  44. responseResult: graphHelper.generateSuccess('System Flags applied successfully')
  45. }
  46. },
  47. async resetTelemetryClientId (obj, args, context) {
  48. try {
  49. WIKI.telemetry.generateClientId()
  50. await WIKI.configSvc.saveToDb(['telemetry'])
  51. return {
  52. responseResult: graphHelper.generateSuccess('Telemetry state updated successfully')
  53. }
  54. } catch (err) {
  55. return graphHelper.generateError(err)
  56. }
  57. },
  58. async setTelemetry (obj, args, context) {
  59. try {
  60. _.set(WIKI.config, 'telemetry.isEnabled', args.enabled)
  61. WIKI.telemetry.enabled = args.enabled
  62. await WIKI.configSvc.saveToDb(['telemetry'])
  63. return {
  64. responseResult: graphHelper.generateSuccess('Telemetry Client ID has been reset successfully')
  65. }
  66. } catch (err) {
  67. return graphHelper.generateError(err)
  68. }
  69. },
  70. async performUpgrade (obj, args, context) {
  71. try {
  72. if (process.env.UPGRADE_COMPANION) {
  73. await request({
  74. method: 'POST',
  75. uri: 'http://wiki-update-companion/upgrade'
  76. })
  77. return {
  78. responseResult: graphHelper.generateSuccess('Upgrade has started.')
  79. }
  80. } else {
  81. throw new Error('You must run the wiki-update-companion container and pass the UPGRADE_COMPANION env var in order to use this feature.')
  82. }
  83. } catch (err) {
  84. return graphHelper.generateError(err)
  85. }
  86. },
  87. /**
  88. * Import Users from a v1 installation
  89. */
  90. async importUsersFromV1(obj, args, context) {
  91. try {
  92. const MongoClient = require('mongodb').MongoClient
  93. if (args.mongoDbConnString && args.mongoDbConnString.length > 10) {
  94. // -> Connect to DB
  95. const client = await MongoClient.connect(args.mongoDbConnString, {
  96. appname: `Wiki.js ${WIKI.version} Migration Tool`
  97. })
  98. const dbUsers = client.db().collection('users')
  99. const userCursor = dbUsers.find({ email: { '$ne': 'guest' } })
  100. const curDateISO = new Date().toISOString()
  101. let failed = []
  102. let usersCount = 0
  103. let groupsCount = 0
  104. let assignableGroups = []
  105. let reuseGroups = []
  106. // -> Create SINGLE group
  107. if (args.groupMode === `SINGLE`) {
  108. const singleGroup = await WIKI.models.groups.query().insert({
  109. name: `Import_${curDateISO}`,
  110. permissions: JSON.stringify(WIKI.data.groups.defaultPermissions),
  111. pageRules: JSON.stringify(WIKI.data.groups.defaultPageRules)
  112. })
  113. groupsCount++
  114. assignableGroups.push(singleGroup.id)
  115. }
  116. // -> Iterate all users
  117. while (await userCursor.hasNext()) {
  118. const usr = await userCursor.next()
  119. let usrGroup = []
  120. if (args.groupMode === `MULTI`) {
  121. // -> Check if global admin
  122. if (_.some(usr.rights, ['role', 'admin'])) {
  123. usrGroup.push(1)
  124. } else {
  125. // -> Check if identical group already exists
  126. const currentRights = _.sortBy(_.map(usr.rights, r => _.pick(r, ['role', 'path', 'exact', 'deny'])), ['role', 'path', 'exact', 'deny'])
  127. const ruleSetId = crypto.createHash('sha1').update(JSON.stringify(currentRights)).digest('base64')
  128. const existingGroup = _.find(reuseGroups, ['hash', ruleSetId])
  129. if (existingGroup) {
  130. usrGroup.push(existingGroup.groupId)
  131. } else {
  132. // -> Build new group
  133. const pageRules = _.map(usr.rights, r => {
  134. let roles = ['read:pages', 'read:assets', 'read:comments', 'write:comments']
  135. if (r.role === `write`) {
  136. roles = _.concat(roles, ['write:pages', 'manage:pages', 'read:source', 'read:history', 'write:assets', 'manage:assets'])
  137. }
  138. return {
  139. id: nanoid('1234567890abcdef', 10),
  140. roles: roles,
  141. match: r.exact ? 'EXACT' : 'START',
  142. deny: r.deny,
  143. path: (r.path.indexOf('/') === 0) ? r.path.substring(1) : r.path,
  144. locales: []
  145. }
  146. })
  147. const perms = _.chain(pageRules).reject('deny').map('roles').union().flatten().value()
  148. // -> Create new group
  149. const newGroup = await WIKI.models.groups.query().insert({
  150. name: `Import_${curDateISO}_${groupsCount + 1}`,
  151. permissions: JSON.stringify(perms),
  152. pageRules: JSON.stringify(pageRules)
  153. })
  154. reuseGroups.push({
  155. groupId: newGroup.id,
  156. hash: ruleSetId
  157. })
  158. groupsCount++
  159. usrGroup.push(newGroup.id)
  160. }
  161. }
  162. }
  163. // -> Create User
  164. try {
  165. await WIKI.models.users.createNewUser({
  166. providerKey: usr.provider,
  167. email: usr.email,
  168. name: usr.name,
  169. passwordRaw: usr.password,
  170. groups: (usrGroup.length > 0) ? usrGroup : assignableGroups,
  171. mustChangePassword: false,
  172. sendWelcomeEmail: false
  173. })
  174. usersCount++
  175. } catch (err) {
  176. failed.push({
  177. provider: usr.provider,
  178. email: usr.email,
  179. error: err.message
  180. })
  181. WIKI.logger.warn(`${usr.email}: ${err}`)
  182. }
  183. }
  184. // -> Reload group permissions
  185. if (args.groupMode !== `NONE`) {
  186. await WIKI.auth.reloadGroups()
  187. }
  188. client.close()
  189. return {
  190. responseResult: graphHelper.generateSuccess('Import completed.'),
  191. usersCount: usersCount,
  192. groupsCount: groupsCount,
  193. failed: failed
  194. }
  195. } else {
  196. throw new Error('MongoDB Connection String is missing or invalid.')
  197. }
  198. } catch (err) {
  199. return graphHelper.generateError(err)
  200. }
  201. }
  202. },
  203. SystemInfo: {
  204. configFile () {
  205. return path.join(process.cwd(), 'config.yml')
  206. },
  207. cpuCores () {
  208. return os.cpus().length
  209. },
  210. currentVersion () {
  211. return WIKI.version
  212. },
  213. dbType () {
  214. return _.get(dbTypes, WIKI.config.db.type, 'Unknown DB')
  215. },
  216. async dbVersion () {
  217. let version = 'Unknown Version'
  218. switch (WIKI.config.db.type) {
  219. case 'mariadb':
  220. case 'mysql':
  221. const resultMYSQL = await WIKI.models.knex.raw('SELECT VERSION() as version;')
  222. version = _.get(resultMYSQL, '[0][0].version', 'Unknown Version')
  223. break
  224. case 'mssql':
  225. const resultMSSQL = await WIKI.models.knex.raw('SELECT @@VERSION as version;')
  226. version = _.get(resultMSSQL, '[0].version', 'Unknown Version')
  227. break
  228. case 'postgres':
  229. version = _.get(WIKI.models, 'knex.client.version', 'Unknown Version')
  230. break
  231. case 'sqlite':
  232. version = _.get(WIKI.models, 'knex.client.driver.VERSION', 'Unknown Version')
  233. break
  234. }
  235. return version
  236. },
  237. dbHost () {
  238. if (WIKI.config.db.type === 'sqlite') {
  239. return WIKI.config.db.storage
  240. } else {
  241. return WIKI.config.db.host
  242. }
  243. },
  244. hostname () {
  245. return os.hostname()
  246. },
  247. latestVersion () {
  248. return WIKI.system.updates.version
  249. },
  250. latestVersionReleaseDate () {
  251. return moment.utc(WIKI.system.updates.releaseDate)
  252. },
  253. nodeVersion () {
  254. return process.version.substr(1)
  255. },
  256. async operatingSystem () {
  257. let osLabel = `${os.type()} (${os.platform()}) ${os.release()} ${os.arch()}`
  258. if (os.platform() === 'linux') {
  259. const osInfo = await getos()
  260. osLabel = `${os.type()} - ${osInfo.dist} (${osInfo.codename || os.platform()}) ${osInfo.release || os.release()} ${os.arch()}`
  261. }
  262. return osLabel
  263. },
  264. async platform () {
  265. const isDockerized = await fs.pathExists('/.dockerenv')
  266. if (isDockerized) {
  267. return 'docker'
  268. }
  269. return os.platform()
  270. },
  271. ramTotal () {
  272. return filesize(os.totalmem())
  273. },
  274. telemetry () {
  275. return WIKI.telemetry.enabled
  276. },
  277. telemetryClientId () {
  278. return WIKI.config.telemetry.clientId
  279. },
  280. async upgradeCapable () {
  281. return !_.isNil(process.env.UPGRADE_COMPANION)
  282. },
  283. workingDirectory () {
  284. return process.cwd()
  285. },
  286. async groupsTotal () {
  287. const total = await WIKI.models.groups.query().count('* as total').first().pluck('total')
  288. return _.toSafeInteger(total)
  289. },
  290. async pagesTotal () {
  291. const total = await WIKI.models.pages.query().count('* as total').first().pluck('total')
  292. return _.toSafeInteger(total)
  293. },
  294. async usersTotal () {
  295. const total = await WIKI.models.users.query().count('* as total').first().pluck('total')
  296. return _.toSafeInteger(total)
  297. }
  298. }
  299. }