@ -5,257 +5,55 @@ const os = require('os')
const filesize = require ( 'filesize' )
const path = require ( 'path' )
const fs = require ( 'fs-extra' )
const moment = require ( 'moment ')
const { DateTime } = require ( 'luxon ')
const graphHelper = require ( '../../helpers/graph' )
const request = require ( 'request-promise' )
const crypto = require ( 'crypto' )
const nanoid = require ( 'nanoid/non-secure' ) . customAlphabet ( '1234567890abcdef' , 10 )
/* global WIKI */
const dbTypes = {
mysql : 'MySQL' ,
mariadb : 'MariaDB' ,
postgres : 'PostgreSQL' ,
sqlite : 'SQLite' ,
mssql : 'MS SQL Server'
}
module . exports = {
Query : {
async system ( ) { return { } }
} ,
Mutation : {
async system ( ) { return { } }
} ,
SystemQuery : {
flags ( ) {
systemFlags ( ) {
return _ . transform ( WIKI . config . flags , ( result , value , key ) => {
result . push ( { key , value } )
} , [ ] )
} ,
async i nfo ( ) { return { } } ,
async extensions ( ) {
const exts = Object . values ( WIKI . extensions . ext ) . map ( ext => _ . pick ( ext , [ 'key' , 'title' , 'description' , 'isInstalled' ]) )
for ( le t ext of exts ) {
async systemInfo ( ) { return { } } ,
async systemExtensions ( ) {
const exts = Object . values ( WIKI . extensions . ext ) . map ( ext => _ . pick ( ext , [ 'key' , 'title' , 'description' , 'isInstalled' , 'isInstallable' ]) )
for ( cons t ext of exts ) {
ext . isCompatible = await WIKI . extensions . ext [ ext . key ] . isCompatible ( )
}
return exts
} ,
systemSecurity ( ) {
return WIKI . config . security
}
} ,
SystemMutation : {
async update Flags ( obj , args , context ) {
Mutation: {
async update System Flags ( obj , args , context ) {
WIKI . config . flags = _ . transform ( args . flags , ( result , row ) => {
_ . set ( result , row . key , row . value )
} , { } )
await WIKI . configSvc . applyFlags ( )
await WIKI . configSvc . saveToDb ( [ 'flags' ] )
return {
responseResult : graphHelper . generateSuccess ( 'System Flags applied successfully' )
}
} ,
async resetTelemetryClientId ( obj , args , context ) {
try {
WIKI . telemetry . generateClientId ( )
await WIKI . configSvc . saveToDb ( [ 'telemetry' ] )
return {
responseResult : graphHelper . generateSuccess ( 'Telemetry state updated successfully' )
}
} catch ( err ) {
return graphHelper . generateError ( err )
}
} ,
async setTelemetry ( obj , args , context ) {
try {
_ . set ( WIKI . config , 'telemetry.isEnabled' , args . enabled )
WIKI . telemetry . enabled = args . enabled
await WIKI . configSvc . saveToDb ( [ 'telemetry' ] )
return {
responseResult : graphHelper . generateSuccess ( 'Telemetry Client ID has been reset successfully' )
}
} catch ( err ) {
return graphHelper . generateError ( err )
}
} ,
async performUpgrade ( obj , args , context ) {
try {
if ( process . env . UPGRADE _COMPANION ) {
await request ( {
method : 'POST' ,
uri : 'http://wiki-update-companion/upgrade'
} )
return {
responseResult : graphHelper . generateSuccess ( 'Upgrade has started.' )
}
} else {
throw new Error ( 'You must run the wiki-update-companion container and pass the UPGRADE_COMPANION env var in order to use this feature.' )
}
} catch ( err ) {
return graphHelper . generateError ( err )
}
} ,
/ * *
* Import Users from a v1 installation
* /
async importUsersFromV1 ( obj , args , context ) {
try {
const MongoClient = require ( 'mongodb' ) . MongoClient
if ( args . mongoDbConnString && args . mongoDbConnString . length > 10 ) {
// -> Connect to DB
const client = await MongoClient . connect ( args . mongoDbConnString , {
appname : ` Wiki.js ${ WIKI . version } Migration Tool `
} )
const dbUsers = client . db ( ) . collection ( 'users' )
const userCursor = dbUsers . find ( { email : { '$ne' : 'guest' } } )
const curDateISO = new Date ( ) . toISOString ( )
let failed = [ ]
let usersCount = 0
let groupsCount = 0
let assignableGroups = [ ]
let reuseGroups = [ ]
// -> Create SINGLE group
if ( args . groupMode === ` SINGLE ` ) {
const singleGroup = await WIKI . models . groups . query ( ) . insert ( {
name : ` Import_ ${ curDateISO } ` ,
permissions : JSON . stringify ( WIKI . data . groups . defaultPermissions ) ,
pageRules : JSON . stringify ( WIKI . data . groups . defaultPageRules )
} )
groupsCount ++
assignableGroups . push ( singleGroup . id )
}
// -> Iterate all users
while ( await userCursor . hasNext ( ) ) {
const usr = await userCursor . next ( )
let usrGroup = [ ]
if ( args . groupMode === ` MULTI ` ) {
// -> Check if global admin
if ( _ . some ( usr . rights , [ 'role' , 'admin' ] ) ) {
usrGroup . push ( 1 )
} else {
// -> Check if identical group already exists
const currentRights = _ . sortBy ( _ . map ( usr . rights , r => _ . pick ( r , [ 'role' , 'path' , 'exact' , 'deny' ] ) ) , [ 'role' , 'path' , 'exact' , 'deny' ] )
const ruleSetId = crypto . createHash ( 'sha1' ) . update ( JSON . stringify ( currentRights ) ) . digest ( 'base64' )
const existingGroup = _ . find ( reuseGroups , [ 'hash' , ruleSetId ] )
if ( existingGroup ) {
usrGroup . push ( existingGroup . groupId )
} else {
// -> Build new group
const pageRules = _ . map ( usr . rights , r => {
let roles = [ 'read:pages' , 'read:assets' , 'read:comments' , 'write:comments' ]
if ( r . role === ` write ` ) {
roles = _ . concat ( roles , [ 'write:pages' , 'manage:pages' , 'read:source' , 'read:history' , 'write:assets' , 'manage:assets' ] )
}
return {
id : nanoid ( ) ,
roles : roles ,
match : r . exact ? 'EXACT' : 'START' ,
deny : r . deny ,
path : ( r . path . indexOf ( '/' ) === 0 ) ? r . path . substring ( 1 ) : r . path ,
locales : [ ]
}
} )
const perms = _ . chain ( pageRules ) . reject ( 'deny' ) . map ( 'roles' ) . union ( ) . flatten ( ) . value ( )
// -> Create new group
const newGroup = await WIKI . models . groups . query ( ) . insert ( {
name : ` Import_ ${ curDateISO } _ ${ groupsCount + 1 } ` ,
permissions : JSON . stringify ( perms ) ,
pageRules : JSON . stringify ( pageRules )
} )
reuseGroups . push ( {
groupId : newGroup . id ,
hash : ruleSetId
} )
groupsCount ++
usrGroup . push ( newGroup . id )
}
}
}
// -> Create User
try {
await WIKI . models . users . createNewUser ( {
providerKey : usr . provider ,
email : usr . email ,
name : usr . name ,
passwordRaw : usr . password ,
groups : ( usrGroup . length > 0 ) ? usrGroup : assignableGroups ,
mustChangePassword : false ,
sendWelcomeEmail : false
} )
usersCount ++
} catch ( err ) {
failed . push ( {
provider : usr . provider ,
email : usr . email ,
error : err . message
} )
WIKI . logger . warn ( ` ${ usr . email } : ${ err } ` )
}
}
// -> Reload group permissions
if ( args . groupMode !== ` NONE ` ) {
await WIKI . auth . reloadGroups ( )
WIKI . events . outbound . emit ( 'reloadGroups' )
}
client . close ( )
return {
responseResult : graphHelper . generateSuccess ( 'Import completed.' ) ,
usersCount : usersCount ,
groupsCount : groupsCount ,
failed : failed
}
} else {
throw new Error ( 'MongoDB Connection String is missing or invalid.' )
}
} catch ( err ) {
return graphHelper . generateError ( err )
status : graphHelper . generateSuccess ( 'System Flags applied successfully' )
}
} ,
/ * *
* Set HTTPS Redirection State
* /
async setHTTPSRedirection ( obj , args , context ) {
_ . set ( WIKI . config , 'server.sslRedir' , args . enabled )
await WIKI . configSvc . saveToDb ( [ 'server' ] )
async updateSystemSecurity ( obj , args , context ) {
WIKI . config . security = _ . defaultsDeep ( _ . omit ( args , [ '__typename' ] ) , WIKI . config . security )
// TODO: broadcast config update
await WIKI . configSvc . saveToDb ( [ 'security' ] )
return {
re sponseResul t: graphHelper . generateSuccess ( ' HTTP Redirection state set successfully. ')
status : graphHelper . generateSuccess ( 'System Security configuration applied successfully' )
}
} ,
/ * *
* Renew SSL Certificate
* /
async renewHTTPSCertificate ( obj , args , context ) {
async installExtension ( obj , args , context ) {
try {
if ( ! WIKI . config . ssl . enabled ) {
throw new WIKI . Error . SystemSSLDisabled ( )
} else if ( WIKI . config . ssl . provider !== ` letsencrypt ` ) {
throw new WIKI . Error . SystemSSLRenewInvalidProvider ( )
} else if ( ! WIKI . servers . le ) {
throw new WIKI . Error . SystemSSLLEUnavailable ( )
} else {
await WIKI . servers . le . requestCertificate ( )
await WIKI . servers . restartServer ( 'https' )
await WIKI . extensions . ext [ args . key ] . install ( )
// TODO: broadcast ext install
return {
responseResult : graphHelper . generateSuccess ( 'SSL Certificate renewed successfully.' )
}
status : graphHelper . generateSuccess ( 'Extension installed successfully' )
}
} catch ( err ) {
return graphHelper . generateError ( err )
@ -272,36 +70,11 @@ module.exports = {
currentVersion ( ) {
return WIKI . version
} ,
dbType ( ) {
return _ . get ( dbTypes , WIKI . config . db . type , 'Unknown DB' )
} ,
async dbVersion ( ) {
let version = 'Unknown Version'
switch ( WIKI . config . db . type ) {
case 'mariadb' :
case 'mysql' :
const resultMYSQL = await WIKI . models . knex . raw ( 'SELECT VERSION() as version;' )
version = _ . get ( resultMYSQL , '[0][0].version' , 'Unknown Version' )
break
case 'mssql' :
const resultMSSQL = await WIKI . models . knex . raw ( 'SELECT @@VERSION as version;' )
version = _ . get ( resultMSSQL , '[0].version' , 'Unknown Version' )
break
case 'postgres' :
version = _ . get ( WIKI . models , 'knex.client.version' , 'Unknown Version' )
break
case 'sqlite' :
version = _ . get ( WIKI . models , 'knex.client.driver.VERSION' , 'Unknown Version' )
break
}
return version
return _ . get ( WIKI . models , 'knex.client.version' , 'Unknown Version' )
} ,
dbHost ( ) {
if ( WIKI . config . db . type === 'sqlite' ) {
return WIKI . config . db . storage
} else {
return WIKI . config . db . host
}
} ,
hostname ( ) {
return os . hostname ( )
@ -319,7 +92,7 @@ module.exports = {
return WIKI . system . updates . version
} ,
latestVersionReleaseDate ( ) {
return moment. utc ( WIKI . system . updates . releaseDate )
return DateTime. fromISO ( WIKI . system . updates . releaseDate ) . toJSDate ( )
} ,
nodeVersion ( ) {
return process . version . substr ( 1 )
@ -343,10 +116,10 @@ module.exports = {
return filesize ( os . totalmem ( ) )
} ,
sslDomain ( ) {
return WIKI . config . ssl . enabled && WIKI . config . ssl . provider === ` letsencrypt ` ? WIKI . config . ssl . domain : null
return WIKI . config . ssl . enabled && WIKI . config . ssl . provider === 'letsencrypt' ? WIKI . config . ssl . domain : null
} ,
sslExpirationDate ( ) {
return WIKI . config . ssl . enabled && WIKI . config . ssl . provider === ` letsencrypt ` ? _ . get ( WIKI . config . letsencrypt , 'payload.expires' , null ) : null
return WIKI . config . ssl . enabled && WIKI . config . ssl . provider === 'letsencrypt' ? _ . get ( WIKI . config . letsencrypt , 'payload.expires' , null ) : null
} ,
sslProvider ( ) {
return WIKI . config . ssl . enabled ? WIKI . config . ssl . provider : null
@ -355,7 +128,7 @@ module.exports = {
return 'OK'
} ,
sslSubscriberEmail ( ) {
return WIKI . config . ssl . enabled && WIKI . config . ssl . provider === ` letsencrypt ` ? WIKI . config . ssl . subscriberEmail : null
return WIKI . config . ssl . enabled && WIKI . config . ssl . provider === 'letsencrypt' ? WIKI . config . ssl . subscriberEmail : null
} ,
telemetry ( ) {
return WIKI . telemetry . enabled