mirror of
https://github.com/requarks/wiki.git
synced 2026-08-26 21:27:10 -05:00
refactor: update schema with new structure / naming
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
const { SchemaDirectiveVisitor } = require('graphql-tools')
|
||||
const { defaultFieldResolver } = require('graphql')
|
||||
const _ = require('lodash')
|
||||
|
||||
class AuthDirective extends SchemaDirectiveVisitor {
|
||||
visitObject(type) {
|
||||
this.ensureFieldsWrapped(type)
|
||||
type._requiredAuthScopes = this.args.requires
|
||||
}
|
||||
// Visitor methods for nested types like fields and arguments
|
||||
// also receive a details object that provides information about
|
||||
// the parent and grandparent types.
|
||||
visitFieldDefinition(field, details) {
|
||||
this.ensureFieldsWrapped(details.objectType)
|
||||
field._requiredAuthScopes = this.args.requires
|
||||
}
|
||||
|
||||
visitArgumentDefinition(argument, details) {
|
||||
this.ensureFieldsWrapped(details.objectType)
|
||||
argument._requiredAuthScopes = this.args.requires
|
||||
}
|
||||
|
||||
ensureFieldsWrapped(objectType) {
|
||||
// Mark the GraphQLObjectType object to avoid re-wrapping:
|
||||
if (objectType._authFieldsWrapped) return
|
||||
objectType._authFieldsWrapped = true
|
||||
|
||||
const fields = objectType.getFields()
|
||||
|
||||
Object.keys(fields).forEach(fieldName => {
|
||||
const field = fields[fieldName]
|
||||
const { resolve = defaultFieldResolver } = field
|
||||
field.resolve = async function (...args) {
|
||||
// Get the required scopes from the field first, falling back
|
||||
// to the objectType if no scopes is required by the field:
|
||||
const requiredScopes = field._requiredAuthScopes || objectType._requiredAuthScopes
|
||||
|
||||
if (!requiredScopes) {
|
||||
return resolve.apply(this, args)
|
||||
}
|
||||
|
||||
const context = args[2]
|
||||
if (!context.req.user) {
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
if (!_.some(context.req.user.permissions, pm => _.includes(requiredScopes, pm))) {
|
||||
throw new Error('Forbidden')
|
||||
}
|
||||
|
||||
return resolve.apply(this, args)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthDirective
|
||||
@@ -1,5 +0,0 @@
|
||||
const { createRateLimitDirective } = require('graphql-rate-limit-directive')
|
||||
|
||||
module.exports = createRateLimitDirective({
|
||||
keyGenerator: (directiveArgs, source, args, context, info) => `${context.req.ip}:${info.parentType}.${info.fieldName}`
|
||||
})
|
||||
+14
-3
@@ -3,16 +3,20 @@ const fs = require('fs')
|
||||
const path = require('path')
|
||||
const autoload = require('auto-load')
|
||||
const { makeExecutableSchema } = require('@graphql-tools/schema')
|
||||
const { rateLimitDirective } = require('graphql-rate-limit-directive')
|
||||
const { defaultKeyGenerator, rateLimitDirective } = require('graphql-rate-limit-directive')
|
||||
const { GraphQLUpload } = require('graphql-upload')
|
||||
const { rateLimitDirectiveTypeDefs, rateLimitDirectiveTransformer } = rateLimitDirective()
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
WIKI.logger.info(`Loading GraphQL Schema...`)
|
||||
// Rate Limiter
|
||||
|
||||
const { rateLimitDirectiveTypeDefs, rateLimitDirectiveTransformer } = rateLimitDirective({
|
||||
keyGenerator: (directiveArgs, source, args, context, info) => `${context.req.ip}:${defaultKeyGenerator(directiveArgs, source, args, context, info)}`
|
||||
})
|
||||
|
||||
// Schemas
|
||||
|
||||
WIKI.logger.info(`Loading GraphQL Schema...`)
|
||||
const typeDefs = [
|
||||
rateLimitDirectiveTypeDefs
|
||||
]
|
||||
@@ -23,7 +27,11 @@ schemas.forEach(schema => {
|
||||
|
||||
// Resolvers
|
||||
|
||||
WIKI.logger.info(`Loading GraphQL Resolvers...`)
|
||||
let resolvers = {
|
||||
Date: require('./scalars/date'),
|
||||
JSON: require('./scalars/json'),
|
||||
UUID: require('./scalars/uuid'),
|
||||
Upload: GraphQLUpload
|
||||
}
|
||||
const resolversObj = _.values(autoload(path.join(WIKI.SERVERPATH, 'graph/resolvers')))
|
||||
@@ -33,11 +41,14 @@ resolversObj.forEach(resolver => {
|
||||
|
||||
// Make executable schema
|
||||
|
||||
WIKI.logger.info(`Compiling GraphQL Schema...`)
|
||||
let schema = makeExecutableSchema({
|
||||
typeDefs,
|
||||
resolvers
|
||||
})
|
||||
|
||||
// Apply schema transforms
|
||||
|
||||
schema = rateLimitDirectiveTransformer(schema)
|
||||
|
||||
WIKI.logger.info(`GraphQL Schema: [ OK ]`)
|
||||
|
||||
@@ -5,13 +5,7 @@ const graphHelper = require('../../helpers/graph')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async analytics() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async analytics() { return {} }
|
||||
},
|
||||
AnalyticsQuery: {
|
||||
async providers(obj, args, context, info) {
|
||||
async analyticsProviders(obj, args, context, info) {
|
||||
let providers = await WIKI.models.analytics.getProviders(args.isEnabled)
|
||||
providers = providers.map(stg => {
|
||||
const providerInfo = _.find(WIKI.data.analytics, ['key', stg.key]) || {}
|
||||
@@ -33,8 +27,8 @@ module.exports = {
|
||||
return providers
|
||||
}
|
||||
},
|
||||
AnalyticsMutation: {
|
||||
async updateProviders(obj, args, context) {
|
||||
Mutation: {
|
||||
async updateAnalyticsProviders(obj, args, context) {
|
||||
try {
|
||||
for (let str of args.providers) {
|
||||
await WIKI.models.analytics.query().patch({
|
||||
|
||||
@@ -7,13 +7,7 @@ const assetHelper = require('../../helpers/asset')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async assets() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async assets() { return {} }
|
||||
},
|
||||
AssetQuery: {
|
||||
async list(obj, args, context) {
|
||||
async assets(obj, args, context) {
|
||||
let cond = {
|
||||
folderId: args.folderId === 0 ? null : args.folderId
|
||||
}
|
||||
@@ -31,7 +25,7 @@ module.exports = {
|
||||
kind: a.kind.toUpperCase()
|
||||
}))
|
||||
},
|
||||
async folders(obj, args, context) {
|
||||
async assetsFolders(obj, args, context) {
|
||||
const results = await WIKI.models.assetFolders.query().where({
|
||||
parentId: args.parentFolderId === 0 ? null : args.parentFolderId
|
||||
})
|
||||
@@ -43,11 +37,11 @@ module.exports = {
|
||||
})
|
||||
}
|
||||
},
|
||||
AssetMutation: {
|
||||
Mutation: {
|
||||
/**
|
||||
* Create New Asset Folder
|
||||
*/
|
||||
async createFolder(obj, args, context) {
|
||||
async createAssetsFolder(obj, args, context) {
|
||||
try {
|
||||
const folderSlug = sanitize(args.slug).toLowerCase()
|
||||
const parentFolderId = args.parentFolderId === 0 ? null : args.parentFolderId
|
||||
|
||||
@@ -7,12 +7,6 @@ const graphHelper = require('../../helpers/graph')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async authentication () { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async authentication () { return {} }
|
||||
},
|
||||
AuthenticationQuery: {
|
||||
/**
|
||||
* List of API Keys
|
||||
*/
|
||||
@@ -34,7 +28,7 @@ module.exports = {
|
||||
apiState () {
|
||||
return WIKI.config.api.isEnabled
|
||||
},
|
||||
async strategies () {
|
||||
async authStrategies () {
|
||||
return WIKI.data.authentication.map(stg => ({
|
||||
...stg,
|
||||
isAvailable: stg.isAvailable === true,
|
||||
@@ -45,35 +39,35 @@ module.exports = {
|
||||
})
|
||||
}, []), 'key')
|
||||
}))
|
||||
},
|
||||
/**
|
||||
* Fetch active authentication strategies
|
||||
*/
|
||||
async activeStrategies (obj, args, context, info) {
|
||||
let strategies = await WIKI.models.authentication.getStrategies()
|
||||
strategies = strategies.map(stg => {
|
||||
const strategyInfo = _.find(WIKI.data.authentication, ['key', stg.strategyKey]) || {}
|
||||
return {
|
||||
...stg,
|
||||
strategy: strategyInfo,
|
||||
config: _.sortBy(_.transform(stg.config, (res, value, key) => {
|
||||
const configData = _.get(strategyInfo.props, key, false)
|
||||
if (configData) {
|
||||
res.push({
|
||||
key,
|
||||
value: JSON.stringify({
|
||||
...configData,
|
||||
value
|
||||
})
|
||||
})
|
||||
}
|
||||
}, []), 'key')
|
||||
}
|
||||
})
|
||||
return args.enabledOnly ? _.filter(strategies, 'isEnabled') : strategies
|
||||
}
|
||||
// /**
|
||||
// * Fetch active authentication strategies
|
||||
// */
|
||||
// async activeStrategies (obj, args, context, info) {
|
||||
// let strategies = await WIKI.models.authentication.getStrategies()
|
||||
// strategies = strategies.map(stg => {
|
||||
// const strategyInfo = _.find(WIKI.data.authentication, ['key', stg.strategyKey]) || {}
|
||||
// return {
|
||||
// ...stg,
|
||||
// strategy: strategyInfo,
|
||||
// config: _.sortBy(_.transform(stg.config, (res, value, key) => {
|
||||
// const configData = _.get(strategyInfo.props, key, false)
|
||||
// if (configData) {
|
||||
// res.push({
|
||||
// key,
|
||||
// value: JSON.stringify({
|
||||
// ...configData,
|
||||
// value
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// }, []), 'key')
|
||||
// }
|
||||
// })
|
||||
// return args.enabledOnly ? _.filter(strategies, 'isEnabled') : strategies
|
||||
// }
|
||||
},
|
||||
AuthenticationMutation: {
|
||||
Mutation: {
|
||||
/**
|
||||
* Create New API Key
|
||||
*/
|
||||
@@ -197,7 +191,7 @@ module.exports = {
|
||||
/**
|
||||
* Update Authentication Strategies
|
||||
*/
|
||||
async updateStrategies (obj, args, context) {
|
||||
async updateAuthStrategies (obj, args, context) {
|
||||
try {
|
||||
const previousStrategies = await WIKI.models.authentication.getStrategies()
|
||||
for (const str of args.strategies) {
|
||||
|
||||
@@ -5,16 +5,10 @@ const graphHelper = require('../../helpers/graph')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async comments() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async comments() { return {} }
|
||||
},
|
||||
CommentQuery: {
|
||||
/**
|
||||
* Fetch list of Comments Providers
|
||||
*/
|
||||
async providers(obj, args, context, info) {
|
||||
async commentsProviders(obj, args, context, info) {
|
||||
const providers = await WIKI.models.commentProviders.getProviders()
|
||||
return providers.map(provider => {
|
||||
const providerInfo = _.find(WIKI.data.commentProviders, ['key', provider.key]) || {}
|
||||
@@ -39,7 +33,7 @@ module.exports = {
|
||||
/**
|
||||
* Fetch list of comments for a page
|
||||
*/
|
||||
async list (obj, args, context) {
|
||||
async comments (obj, args, context) {
|
||||
const page = await WIKI.models.pages.query().select('id').findOne({ localeCode: args.locale, path: args.path })
|
||||
if (page) {
|
||||
if (WIKI.auth.checkAccess(context.req.user, ['read:comments'], args)) {
|
||||
@@ -60,7 +54,7 @@ module.exports = {
|
||||
/**
|
||||
* Fetch a single comment
|
||||
*/
|
||||
async single (obj, args, context) {
|
||||
async commentById (obj, args, context) {
|
||||
const cm = await WIKI.data.commentProvider.getCommentById(args.id)
|
||||
if (!cm || !cm.pageId) {
|
||||
throw new WIKI.Error.CommentNotFound()
|
||||
@@ -86,11 +80,11 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
CommentMutation: {
|
||||
Mutation: {
|
||||
/**
|
||||
* Create New Comment
|
||||
*/
|
||||
async create (obj, args, context) {
|
||||
async createComment (obj, args, context) {
|
||||
try {
|
||||
const cmId = await WIKI.models.comments.postNewComment({
|
||||
...args,
|
||||
@@ -108,7 +102,7 @@ module.exports = {
|
||||
/**
|
||||
* Update an Existing Comment
|
||||
*/
|
||||
async update (obj, args, context) {
|
||||
async updateComment (obj, args, context) {
|
||||
try {
|
||||
const cmRender = await WIKI.models.comments.updateComment({
|
||||
...args,
|
||||
@@ -126,7 +120,7 @@ module.exports = {
|
||||
/**
|
||||
* Delete an Existing Comment
|
||||
*/
|
||||
async delete (obj, args, context) {
|
||||
async deleteComment (obj, args, context) {
|
||||
try {
|
||||
await WIKI.models.comments.deleteComment({
|
||||
id: args.id,
|
||||
@@ -143,7 +137,7 @@ module.exports = {
|
||||
/**
|
||||
* Update Comments Providers
|
||||
*/
|
||||
async updateProviders(obj, args, context) {
|
||||
async updateCommentsProviders(obj, args, context) {
|
||||
try {
|
||||
for (let provider of args.providers) {
|
||||
await WIKI.models.commentProviders.query().patch({
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
const request = require('request-promise')
|
||||
const _ = require('lodash')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async contribute() { return {} }
|
||||
},
|
||||
ContributeQuery: {
|
||||
async contributors(obj, args, context, info) {
|
||||
try {
|
||||
const resp = await request({
|
||||
method: 'POST',
|
||||
uri: 'https://graph.requarks.io',
|
||||
json: true,
|
||||
body: {
|
||||
query: '{\n sponsors {\n list(kind: BACKER) {\n id\n source\n name\n joined\n website\n twitter\n avatar\n }\n }\n}\n',
|
||||
variables: {}
|
||||
}
|
||||
})
|
||||
return _.get(resp, 'data.sponsors.list', [])
|
||||
} catch (err) {
|
||||
WIKI.logger.warn(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
module.exports = {
|
||||
// Query: {
|
||||
// folders(obj, args, context, info) {
|
||||
// return WIKI.models.Folder.findAll({ where: args })
|
||||
// }
|
||||
// },
|
||||
// Mutation: {
|
||||
// createFolder(obj, args) {
|
||||
// return WIKI.models.Folder.create(args)
|
||||
// },
|
||||
// deleteFolder(obj, args) {
|
||||
// return WIKI.models.Folder.destroy({
|
||||
// where: {
|
||||
// id: args.id
|
||||
// },
|
||||
// limit: 1
|
||||
// })
|
||||
// },
|
||||
// renameFolder(obj, args) {
|
||||
// return WIKI.models.Folder.update({
|
||||
// name: args.name
|
||||
// }, {
|
||||
// where: { id: args.id }
|
||||
// })
|
||||
// }
|
||||
// },
|
||||
// Folder: {
|
||||
// files(grp) {
|
||||
// return grp.getFiles()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -7,16 +7,10 @@ const gql = require('graphql')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async groups () { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async groups () { return {} }
|
||||
},
|
||||
GroupQuery: {
|
||||
/**
|
||||
* FETCH ALL GROUPS
|
||||
*/
|
||||
async list () {
|
||||
async groups () {
|
||||
return WIKI.models.groups.query().select(
|
||||
'groups.*',
|
||||
WIKI.models.groups.relatedQuery('users').count().as('userCount')
|
||||
@@ -25,15 +19,15 @@ module.exports = {
|
||||
/**
|
||||
* FETCH A SINGLE GROUP
|
||||
*/
|
||||
async single(obj, args) {
|
||||
async groupById(obj, args) {
|
||||
return WIKI.models.groups.query().findById(args.id)
|
||||
}
|
||||
},
|
||||
GroupMutation: {
|
||||
Mutation: {
|
||||
/**
|
||||
* ASSIGN USER TO GROUP
|
||||
*/
|
||||
async assignUser (obj, args, { req }) {
|
||||
async assignUserToGroup (obj, args, { req }) {
|
||||
// Check for guest user
|
||||
if (args.userId === 2) {
|
||||
throw new gql.GraphQLError('Cannot assign the Guest user to a group.')
|
||||
@@ -85,7 +79,7 @@ module.exports = {
|
||||
/**
|
||||
* CREATE NEW GROUP
|
||||
*/
|
||||
async create (obj, args, { req }) {
|
||||
async createGroup (obj, args, { req }) {
|
||||
const group = await WIKI.models.groups.query().insertAndFetch({
|
||||
name: args.name,
|
||||
permissions: JSON.stringify(WIKI.data.groups.defaultPermissions),
|
||||
@@ -102,7 +96,7 @@ module.exports = {
|
||||
/**
|
||||
* DELETE GROUP
|
||||
*/
|
||||
async delete (obj, args) {
|
||||
async deleteGroup (obj, args) {
|
||||
if (args.id === 1 || args.id === 2) {
|
||||
throw new gql.GraphQLError('Cannot delete this group.')
|
||||
}
|
||||
@@ -122,7 +116,7 @@ module.exports = {
|
||||
/**
|
||||
* UNASSIGN USER FROM GROUP
|
||||
*/
|
||||
async unassignUser (obj, args) {
|
||||
async unassignUserFromGroup (obj, args) {
|
||||
if (args.userId === 2) {
|
||||
throw new gql.GraphQLError('Cannot unassign Guest user')
|
||||
}
|
||||
@@ -149,7 +143,7 @@ module.exports = {
|
||||
/**
|
||||
* UPDATE GROUP
|
||||
*/
|
||||
async update (obj, args, { req }) {
|
||||
async updateGroup (obj, args, { req }) {
|
||||
// Check for unsafe regex page rules
|
||||
if (_.some(args.pageRules, pr => {
|
||||
return pr.match === 'REGEX' && !safeRegex(pr.path)
|
||||
|
||||
@@ -5,12 +5,6 @@ const _ = require('lodash')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async localization() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async localization() { return {} }
|
||||
},
|
||||
LocalizationQuery: {
|
||||
async locales(obj, args, context, info) {
|
||||
let remoteLocales = await WIKI.cache.get('locales')
|
||||
let localLocales = await WIKI.models.locales.query().select('code', 'isRTL', 'name', 'nativeName', 'createdAt', 'updatedAt', 'availability')
|
||||
@@ -24,19 +18,11 @@ module.exports = {
|
||||
}
|
||||
})
|
||||
},
|
||||
async config(obj, args, context, info) {
|
||||
return {
|
||||
locale: WIKI.config.lang.code,
|
||||
autoUpdate: WIKI.config.lang.autoUpdate,
|
||||
namespacing: WIKI.config.lang.namespacing,
|
||||
namespaces: WIKI.config.lang.namespaces
|
||||
}
|
||||
},
|
||||
translations (obj, args, context, info) {
|
||||
return WIKI.lang.getByNamespace(args.locale, args.namespace)
|
||||
}
|
||||
},
|
||||
LocalizationMutation: {
|
||||
Mutation: {
|
||||
async downloadLocale(obj, args, context) {
|
||||
try {
|
||||
const job = await WIKI.scheduler.registerJob({
|
||||
|
||||
@@ -5,21 +5,15 @@ const graphHelper = require('../../helpers/graph')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async mail() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async mail() { return {} }
|
||||
},
|
||||
MailQuery: {
|
||||
async config(obj, args, context, info) {
|
||||
async mailConfig(obj, args, context, info) {
|
||||
return {
|
||||
...WIKI.config.mail,
|
||||
pass: WIKI.config.mail.pass.length > 0 ? '********' : ''
|
||||
}
|
||||
}
|
||||
},
|
||||
MailMutation: {
|
||||
async sendTest(obj, args, context) {
|
||||
Mutation: {
|
||||
async sendMailTest(obj, args, context) {
|
||||
try {
|
||||
if (_.isEmpty(args.recipientEmail) || args.recipientEmail.length < 6) {
|
||||
throw new WIKI.Error.MailInvalidRecipient()
|
||||
@@ -42,7 +36,7 @@ module.exports = {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async updateConfig(obj, args, context) {
|
||||
async updateMailConfig(obj, args, context) {
|
||||
try {
|
||||
WIKI.config.mail = {
|
||||
senderName: args.senderName,
|
||||
|
||||
@@ -4,21 +4,15 @@ const graphHelper = require('../../helpers/graph')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async navigation () { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async navigation () { return {} }
|
||||
},
|
||||
NavigationQuery: {
|
||||
async tree (obj, args, context, info) {
|
||||
async navigationTree (obj, args, context, info) {
|
||||
return WIKI.models.navigation.getTree({ cache: false, locale: 'all', bypassAuth: true })
|
||||
},
|
||||
config (obj, args, context, info) {
|
||||
navigationConfig (obj, args, context, info) {
|
||||
return WIKI.config.nav
|
||||
}
|
||||
},
|
||||
NavigationMutation: {
|
||||
async updateTree (obj, args, context) {
|
||||
Mutation: {
|
||||
async updateNavigationTree (obj, args, context) {
|
||||
try {
|
||||
await WIKI.models.navigation.query().patch({
|
||||
config: args.tree
|
||||
@@ -34,7 +28,7 @@ module.exports = {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async updateConfig (obj, args, context) {
|
||||
async updateNavigationConfig (obj, args, context) {
|
||||
try {
|
||||
WIKI.config.nav = {
|
||||
mode: args.mode
|
||||
|
||||
@@ -5,16 +5,10 @@ const graphHelper = require('../../helpers/graph')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async pages() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async pages() { return {} }
|
||||
},
|
||||
PageQuery: {
|
||||
/**
|
||||
* PAGE HISTORY
|
||||
*/
|
||||
async history(obj, args, context, info) {
|
||||
async pageHistoryById (obj, args, context, info) {
|
||||
const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.id)
|
||||
if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
|
||||
path: page.path,
|
||||
@@ -32,7 +26,7 @@ module.exports = {
|
||||
/**
|
||||
* PAGE VERSION
|
||||
*/
|
||||
async version(obj, args, context, info) {
|
||||
async pageVersionById (obj, args, context, info) {
|
||||
const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.pageId)
|
||||
if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
|
||||
path: page.path,
|
||||
@@ -49,7 +43,7 @@ module.exports = {
|
||||
/**
|
||||
* SEARCH PAGES
|
||||
*/
|
||||
async search (obj, args, context) {
|
||||
async searchPages (obj, args, context) {
|
||||
if (WIKI.data.searchEngine) {
|
||||
const resp = await WIKI.data.searchEngine.query(args.query, args)
|
||||
return {
|
||||
@@ -73,7 +67,7 @@ module.exports = {
|
||||
/**
|
||||
* LIST PAGES
|
||||
*/
|
||||
async list (obj, args, context, info) {
|
||||
async pages (obj, args, context, info) {
|
||||
let results = await WIKI.models.pages.query().column([
|
||||
'pages.id',
|
||||
'path',
|
||||
@@ -149,7 +143,7 @@ module.exports = {
|
||||
/**
|
||||
* FETCH SINGLE PAGE
|
||||
*/
|
||||
async single (obj, args, context, info) {
|
||||
async pageById (obj, args, context, info) {
|
||||
let page = await WIKI.models.pages.getPageFromDb(args.id)
|
||||
if (page) {
|
||||
if (WIKI.auth.checkAccess(context.req.user, ['manage:pages', 'delete:pages'], {
|
||||
@@ -220,7 +214,7 @@ module.exports = {
|
||||
/**
|
||||
* FETCH PAGE TREE
|
||||
*/
|
||||
async tree (obj, args, context, info) {
|
||||
async pageTree (obj, args, context, info) {
|
||||
let curPage = null
|
||||
|
||||
if (!args.locale) { args.locale = WIKI.config.lang.code }
|
||||
@@ -270,7 +264,7 @@ module.exports = {
|
||||
/**
|
||||
* FETCH PAGE LINKS
|
||||
*/
|
||||
async links (obj, args, context, info) {
|
||||
async pageLinks (obj, args, context, info) {
|
||||
let results
|
||||
|
||||
if (WIKI.config.db.type === 'mysql' || WIKI.config.db.type === 'mariadb' || WIKI.config.db.type === 'sqlite') {
|
||||
@@ -343,7 +337,7 @@ module.exports = {
|
||||
/**
|
||||
* FETCH LATEST VERSION FOR CONFLICT COMPARISON
|
||||
*/
|
||||
async conflictLatest (obj, args, context, info) {
|
||||
async checkConflictsLatest (obj, args, context, info) {
|
||||
let page = await WIKI.models.pages.getPageFromDb(args.id)
|
||||
if (page) {
|
||||
if (WIKI.auth.checkAccess(context.req.user, ['write:pages', 'manage:pages'], {
|
||||
@@ -363,11 +357,11 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
PageMutation: {
|
||||
Mutation: {
|
||||
/**
|
||||
* CREATE PAGE
|
||||
*/
|
||||
async create(obj, args, context) {
|
||||
async createPage(obj, args, context) {
|
||||
try {
|
||||
const page = await WIKI.models.pages.createPage({
|
||||
...args,
|
||||
@@ -384,7 +378,7 @@ module.exports = {
|
||||
/**
|
||||
* UPDATE PAGE
|
||||
*/
|
||||
async update(obj, args, context) {
|
||||
async updatePage(obj, args, context) {
|
||||
try {
|
||||
const page = await WIKI.models.pages.updatePage({
|
||||
...args,
|
||||
@@ -401,7 +395,7 @@ module.exports = {
|
||||
/**
|
||||
* CONVERT PAGE
|
||||
*/
|
||||
async convert(obj, args, context) {
|
||||
async convertPage(obj, args, context) {
|
||||
try {
|
||||
await WIKI.models.pages.convertPage({
|
||||
...args,
|
||||
@@ -415,9 +409,9 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* MOVE PAGE
|
||||
* RENAME PAGE
|
||||
*/
|
||||
async move(obj, args, context) {
|
||||
async renamePage(obj, args, context) {
|
||||
try {
|
||||
await WIKI.models.pages.movePage({
|
||||
...args,
|
||||
@@ -433,7 +427,7 @@ module.exports = {
|
||||
/**
|
||||
* DELETE PAGE
|
||||
*/
|
||||
async delete(obj, args, context) {
|
||||
async deletePage(obj, args, context) {
|
||||
try {
|
||||
await WIKI.models.pages.deletePage({
|
||||
...args,
|
||||
@@ -517,7 +511,7 @@ module.exports = {
|
||||
/**
|
||||
* REBUILD TREE
|
||||
*/
|
||||
async rebuildTree(obj, args, context) {
|
||||
async rebuildPageTree(obj, args, context) {
|
||||
try {
|
||||
await WIKI.models.pages.rebuildTree()
|
||||
return {
|
||||
@@ -530,7 +524,7 @@ module.exports = {
|
||||
/**
|
||||
* RENDER PAGE
|
||||
*/
|
||||
async render (obj, args, context) {
|
||||
async renderPage (obj, args, context) {
|
||||
try {
|
||||
const page = await WIKI.models.pages.query().findById(args.id)
|
||||
if (!page) {
|
||||
@@ -547,7 +541,7 @@ module.exports = {
|
||||
/**
|
||||
* RESTORE PAGE VERSION
|
||||
*/
|
||||
async restore (obj, args, context) {
|
||||
async restorePage (obj, args, context) {
|
||||
try {
|
||||
const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.pageId)
|
||||
if (!page) {
|
||||
@@ -583,7 +577,7 @@ module.exports = {
|
||||
/**
|
||||
* Purge history
|
||||
*/
|
||||
async purgeHistory (obj, args, context) {
|
||||
async purgePagesHistory (obj, args, context) {
|
||||
try {
|
||||
await WIKI.models.pageHistory.purge(args.olderThan)
|
||||
return {
|
||||
|
||||
@@ -5,12 +5,6 @@ const graphHelper = require('../../helpers/graph')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async rendering() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async rendering() { return {} }
|
||||
},
|
||||
RenderingQuery: {
|
||||
async renderers(obj, args, context, info) {
|
||||
let renderers = await WIKI.models.renderers.getRenderers()
|
||||
renderers = renderers.map(rdr => {
|
||||
@@ -37,7 +31,7 @@ module.exports = {
|
||||
return renderers
|
||||
}
|
||||
},
|
||||
RenderingMutation: {
|
||||
Mutation: {
|
||||
async updateRenderers(obj, args, context) {
|
||||
try {
|
||||
for (let rdr of args.renderers) {
|
||||
|
||||
@@ -1,74 +1,10 @@
|
||||
const _ = require('lodash')
|
||||
const graphHelper = require('../../helpers/graph')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async search() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async search() { return {} }
|
||||
},
|
||||
SearchQuery: {
|
||||
async searchEngines(obj, args, context, info) {
|
||||
let searchEngines = await WIKI.models.searchEngines.getSearchEngines()
|
||||
searchEngines = searchEngines.map(searchEngine => {
|
||||
const searchEngineInfo = _.find(WIKI.data.searchEngines, ['key', searchEngine.key]) || {}
|
||||
return {
|
||||
...searchEngineInfo,
|
||||
...searchEngine,
|
||||
config: _.sortBy(_.transform(searchEngine.config, (res, value, key) => {
|
||||
const configData = _.get(searchEngineInfo.props, key, false)
|
||||
if (configData) {
|
||||
res.push({
|
||||
key,
|
||||
value: JSON.stringify({
|
||||
...configData,
|
||||
value
|
||||
})
|
||||
})
|
||||
}
|
||||
}, []), 'key')
|
||||
}
|
||||
})
|
||||
// if (args.filter) { searchEngines = graphHelper.filter(searchEngines, args.filter) }
|
||||
if (args.orderBy) { searchEngines = _.sortBy(searchEngines, [args.orderBy]) }
|
||||
return searchEngines
|
||||
}
|
||||
},
|
||||
SearchMutation: {
|
||||
async updateSearchEngines(obj, args, context) {
|
||||
try {
|
||||
let newActiveEngine = ''
|
||||
for (let searchEngine of args.engines) {
|
||||
if (searchEngine.isEnabled) {
|
||||
newActiveEngine = searchEngine.key
|
||||
}
|
||||
await WIKI.models.searchEngines.query().patch({
|
||||
isEnabled: searchEngine.isEnabled,
|
||||
config: _.reduce(searchEngine.config, (result, value, key) => {
|
||||
_.set(result, `${value.key}`, _.get(JSON.parse(value.value), 'v', null))
|
||||
return result
|
||||
}, {})
|
||||
}).where('key', searchEngine.key)
|
||||
}
|
||||
if (newActiveEngine !== WIKI.data.searchEngine.key) {
|
||||
try {
|
||||
await WIKI.data.searchEngine.deactivate()
|
||||
} catch (err) {
|
||||
WIKI.logger.warn('Failed to deactivate previous search engine:', err)
|
||||
}
|
||||
}
|
||||
await WIKI.models.searchEngines.initEngine({ activate: true })
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('Search Engines updated successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async rebuildIndex (obj, args, context) {
|
||||
async rebuildSearchIndex (obj, args, context) {
|
||||
try {
|
||||
await WIKI.data.searchEngine.rebuild()
|
||||
return {
|
||||
|
||||
@@ -40,9 +40,7 @@ module.exports = {
|
||||
hostname: site.hostname,
|
||||
isEnabled: site.isEnabled
|
||||
} : null
|
||||
},
|
||||
// LEGACY
|
||||
async site() { return {} }
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
/**
|
||||
@@ -178,118 +176,6 @@ module.exports = {
|
||||
return {
|
||||
status: graphHelper.generateSuccess('Site favicon uploaded successfully')
|
||||
}
|
||||
},
|
||||
// LEGACY
|
||||
async site() { return {} }
|
||||
},
|
||||
SiteQuery: {
|
||||
async config(obj, args, context, info) {
|
||||
return {
|
||||
host: WIKI.config.host,
|
||||
title: WIKI.config.title,
|
||||
company: WIKI.config.company,
|
||||
contentLicense: WIKI.config.contentLicense,
|
||||
logoUrl: WIKI.config.logoUrl,
|
||||
...WIKI.config.seo,
|
||||
...WIKI.config.features,
|
||||
...WIKI.config.security,
|
||||
authAutoLogin: WIKI.config.auth.autoLogin,
|
||||
authEnforce2FA: WIKI.config.auth.enforce2FA,
|
||||
authHideLocal: WIKI.config.auth.hideLocal,
|
||||
authLoginBgUrl: WIKI.config.auth.loginBgUrl,
|
||||
authJwtAudience: WIKI.config.auth.audience,
|
||||
authJwtExpiration: WIKI.config.auth.tokenExpiration,
|
||||
authJwtRenewablePeriod: WIKI.config.auth.tokenRenewal,
|
||||
uploadMaxFileSize: WIKI.config.uploads.maxFileSize,
|
||||
uploadMaxFiles: WIKI.config.uploads.maxFiles,
|
||||
uploadScanSVG: WIKI.config.uploads.scanSVG,
|
||||
uploadForceDownload: WIKI.config.uploads.forceDownload
|
||||
}
|
||||
}
|
||||
},
|
||||
SiteMutation: {
|
||||
async updateConfig(obj, args, context) {
|
||||
try {
|
||||
if (args.host) {
|
||||
let siteHost = _.trim(args.host)
|
||||
if (siteHost.endsWith('/')) {
|
||||
siteHost = siteHost.slice(0, -1)
|
||||
}
|
||||
WIKI.config.host = siteHost
|
||||
}
|
||||
|
||||
if (args.title) {
|
||||
WIKI.config.title = _.trim(args.title)
|
||||
}
|
||||
|
||||
if (args.company) {
|
||||
WIKI.config.company = _.trim(args.company)
|
||||
}
|
||||
|
||||
if (args.contentLicense) {
|
||||
WIKI.config.contentLicense = args.contentLicense
|
||||
}
|
||||
|
||||
if (args.logoUrl) {
|
||||
WIKI.config.logoUrl = _.trim(args.logoUrl)
|
||||
}
|
||||
|
||||
WIKI.config.seo = {
|
||||
description: _.get(args, 'description', WIKI.config.seo.description),
|
||||
robots: _.get(args, 'robots', WIKI.config.seo.robots),
|
||||
analyticsService: _.get(args, 'analyticsService', WIKI.config.seo.analyticsService),
|
||||
analyticsId: _.get(args, 'analyticsId', WIKI.config.seo.analyticsId)
|
||||
}
|
||||
|
||||
WIKI.config.auth = {
|
||||
autoLogin: _.get(args, 'authAutoLogin', WIKI.config.auth.autoLogin),
|
||||
enforce2FA: _.get(args, 'authEnforce2FA', WIKI.config.auth.enforce2FA),
|
||||
hideLocal: _.get(args, 'authHideLocal', WIKI.config.auth.hideLocal),
|
||||
loginBgUrl: _.get(args, 'authLoginBgUrl', WIKI.config.auth.loginBgUrl),
|
||||
audience: _.get(args, 'authJwtAudience', WIKI.config.auth.audience),
|
||||
tokenExpiration: _.get(args, 'authJwtExpiration', WIKI.config.auth.tokenExpiration),
|
||||
tokenRenewal: _.get(args, 'authJwtRenewablePeriod', WIKI.config.auth.tokenRenewal)
|
||||
}
|
||||
|
||||
WIKI.config.features = {
|
||||
featurePageRatings: _.get(args, 'featurePageRatings', WIKI.config.features.featurePageRatings),
|
||||
featurePageComments: _.get(args, 'featurePageComments', WIKI.config.features.featurePageComments),
|
||||
featurePersonalWikis: _.get(args, 'featurePersonalWikis', WIKI.config.features.featurePersonalWikis)
|
||||
}
|
||||
|
||||
WIKI.config.security = {
|
||||
securityOpenRedirect: _.get(args, 'securityOpenRedirect', WIKI.config.security.securityOpenRedirect),
|
||||
securityIframe: _.get(args, 'securityIframe', WIKI.config.security.securityIframe),
|
||||
securityReferrerPolicy: _.get(args, 'securityReferrerPolicy', WIKI.config.security.securityReferrerPolicy),
|
||||
securityTrustProxy: _.get(args, 'securityTrustProxy', WIKI.config.security.securityTrustProxy),
|
||||
securitySRI: _.get(args, 'securitySRI', WIKI.config.security.securitySRI),
|
||||
securityHSTS: _.get(args, 'securityHSTS', WIKI.config.security.securityHSTS),
|
||||
securityHSTSDuration: _.get(args, 'securityHSTSDuration', WIKI.config.security.securityHSTSDuration),
|
||||
securityCSP: _.get(args, 'securityCSP', WIKI.config.security.securityCSP),
|
||||
securityCSPDirectives: _.get(args, 'securityCSPDirectives', WIKI.config.security.securityCSPDirectives)
|
||||
}
|
||||
|
||||
WIKI.config.uploads = {
|
||||
maxFileSize: _.get(args, 'uploadMaxFileSize', WIKI.config.uploads.maxFileSize),
|
||||
maxFiles: _.get(args, 'uploadMaxFiles', WIKI.config.uploads.maxFiles),
|
||||
scanSVG: _.get(args, 'uploadScanSVG', WIKI.config.uploads.scanSVG),
|
||||
forceDownload: _.get(args, 'uploadForceDownload', WIKI.config.uploads.forceDownload)
|
||||
}
|
||||
|
||||
await WIKI.configSvc.saveToDb(['host', 'title', 'company', 'contentLicense', 'seo', 'logoUrl', 'auth', 'features', 'security', 'uploads'])
|
||||
|
||||
if (WIKI.config.security.securityTrustProxy) {
|
||||
WIKI.app.enable('trust proxy')
|
||||
} else {
|
||||
WIKI.app.disable('trust proxy')
|
||||
}
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('Site configuration updated successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,227 @@
|
||||
const _ = require('lodash')
|
||||
const graphHelper = require('../../helpers/graph')
|
||||
const { v4: uuid } = require('uuid')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async storage() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async storage() { return {} }
|
||||
},
|
||||
StorageQuery: {
|
||||
async targets(obj, args, context, info) {
|
||||
let targets = await WIKI.models.storage.getTargets()
|
||||
targets = _.sortBy(targets.map(tgt => {
|
||||
const targetInfo = _.find(WIKI.data.storage, ['key', tgt.key]) || {}
|
||||
async storageTargets (obj, args, context, info) {
|
||||
const dbTargets = await WIKI.models.storage.getTargets({ siteId: args.siteId })
|
||||
// targets = _.sortBy(targets.map(tgt => {
|
||||
// const targetInfo = _.find(WIKI.data.storage, ['module', tgt.key]) || {}
|
||||
// return {
|
||||
// ...targetInfo,
|
||||
// ...tgt,
|
||||
// hasSchedule: (targetInfo.schedule !== false),
|
||||
// syncInterval: targetInfo.syncInterval || targetInfo.schedule || 'P0D',
|
||||
// syncIntervalDefault: targetInfo.schedule,
|
||||
// config: _.sortBy(_.transform(tgt.config, (res, value, key) => {
|
||||
// const configData = _.get(targetInfo.props, key, false)
|
||||
// if (configData) {
|
||||
// res.push({
|
||||
// key,
|
||||
// value: JSON.stringify({
|
||||
// ...configData,
|
||||
// value: (configData.sensitive && value.length > 0) ? '********' : value
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// }, []), 'key')
|
||||
// }
|
||||
// }), ['title', 'key'])
|
||||
return _.sortBy(WIKI.storage.defs.map(md => {
|
||||
const dbTarget = dbTargets.find(tg => tg.module === md.key)
|
||||
return {
|
||||
...targetInfo,
|
||||
...tgt,
|
||||
hasSchedule: (targetInfo.schedule !== false),
|
||||
syncInterval: tgt.syncInterval || targetInfo.schedule || 'P0D',
|
||||
syncIntervalDefault: targetInfo.schedule,
|
||||
config: _.sortBy(_.transform(tgt.config, (res, value, key) => {
|
||||
const configData = _.get(targetInfo.props, key, false)
|
||||
if (configData) {
|
||||
res.push({
|
||||
key,
|
||||
value: JSON.stringify({
|
||||
...configData,
|
||||
value: (configData.sensitive && value.length > 0) ? '********' : value
|
||||
id: dbTarget?.id ?? uuid(),
|
||||
isEnabled: dbTarget?.isEnabled ?? false,
|
||||
module: md.key,
|
||||
title: md.title,
|
||||
description: md.description,
|
||||
icon: md.icon,
|
||||
banner: md.banner,
|
||||
vendor: md.vendor,
|
||||
website: md.website,
|
||||
contentTypes: {
|
||||
activeTypes: dbTarget?.contentTypes?.activeTypes ?? md.contentTypes.defaultTypesEnabled,
|
||||
largeThreshold: dbTarget?.contentTypes?.largeThreshold ?? md.contentTypes.defaultLargeThreshold
|
||||
},
|
||||
assetDelivery: {
|
||||
isStreamingSupported: md?.assetDelivery?.isStreamingSupported ?? false,
|
||||
isDirectAccessSupported: md?.assetDelivery?.isDirectAccessSupported ?? false,
|
||||
streaming: dbTarget?.assetDelivery?.streaming ?? md?.assetDelivery?.defaultStreamingEnabled ?? false,
|
||||
directAccess: dbTarget?.assetDelivery?.directAccess ?? md?.assetDelivery?.defaultDirectAccessEnabled ?? false
|
||||
},
|
||||
versioning: {
|
||||
isSupported: md?.versioning?.isSupported ?? false,
|
||||
isForceEnabled: md?.versioning?.isForceEnabled ?? false,
|
||||
enabled: dbTarget?.versioning?.enabled ?? md?.versioning?.defaultEnabled ?? false
|
||||
},
|
||||
sync: {},
|
||||
status: {},
|
||||
setup: {
|
||||
handler: md?.setup?.handler,
|
||||
state: dbTarget?.state?.setup ?? 'notconfigured',
|
||||
values: md.setup?.handler
|
||||
? _.transform(md.setup.defaultValues,
|
||||
(r, v, k) => {
|
||||
r[k] = dbTarget?.config?.[k] ?? v
|
||||
}, {})
|
||||
: {}
|
||||
},
|
||||
config: _.transform(md.props, (r, v, k) => {
|
||||
const cfValue = dbTarget?.config?.[k] ?? v.default
|
||||
r[k] = {
|
||||
...v,
|
||||
value: v.sensitive && cfValue ? '********' : cfValue,
|
||||
...v.enum && {
|
||||
enum: v.enum.map(o => {
|
||||
if (o.indexOf('|') > 0) {
|
||||
const oParsed = o.split('|')
|
||||
return {
|
||||
value: oParsed[0],
|
||||
label: oParsed[1]
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
value: o,
|
||||
label: o
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}, []), 'key')
|
||||
}, {}),
|
||||
actions: md.actions
|
||||
}
|
||||
}), ['title', 'key'])
|
||||
return targets
|
||||
},
|
||||
async status(obj, args, context, info) {
|
||||
let activeTargets = await WIKI.models.storage.query().where('isEnabled', true)
|
||||
return activeTargets.map(tgt => {
|
||||
const targetInfo = _.find(WIKI.data.storage, ['key', tgt.key]) || {}
|
||||
return {
|
||||
key: tgt.key,
|
||||
title: targetInfo.title,
|
||||
status: _.get(tgt, 'state.status', 'pending'),
|
||||
message: _.get(tgt, 'state.message', 'Initializing...'),
|
||||
lastAttempt: _.get(tgt, 'state.lastAttempt', null)
|
||||
}
|
||||
})
|
||||
}), ['title'])
|
||||
}
|
||||
},
|
||||
StorageMutation: {
|
||||
async updateTargets(obj, args, context) {
|
||||
Mutation: {
|
||||
async updateStorageTargets (obj, args, context) {
|
||||
WIKI.logger.debug(`Updating storage targets for site ${args.siteId}...`)
|
||||
try {
|
||||
let dbTargets = await WIKI.models.storage.getTargets()
|
||||
for (let tgt of args.targets) {
|
||||
const currentDbTarget = _.find(dbTargets, ['key', tgt.key])
|
||||
if (!currentDbTarget) {
|
||||
continue
|
||||
const dbTargets = await WIKI.models.storage.getTargets({ siteId: args.siteId })
|
||||
for (const tgt of args.targets) {
|
||||
const md = _.find(WIKI.storage.defs, ['key', tgt.module])
|
||||
if (!md) {
|
||||
throw new Error('Invalid module key for non-existent storage target.')
|
||||
}
|
||||
await WIKI.models.storage.query().patch({
|
||||
isEnabled: tgt.isEnabled,
|
||||
mode: tgt.mode,
|
||||
syncInterval: tgt.syncInterval,
|
||||
config: _.reduce(tgt.config, (result, value, key) => {
|
||||
let configValue = _.get(JSON.parse(value.value), 'v', null)
|
||||
if (configValue === '********') {
|
||||
configValue = _.get(currentDbTarget.config, value.key, '')
|
||||
}
|
||||
_.set(result, `${value.key}`, configValue)
|
||||
return result
|
||||
}, {}),
|
||||
state: {
|
||||
status: 'pending',
|
||||
message: 'Initializing...',
|
||||
lastAttempt: null
|
||||
|
||||
const dbTarget = _.find(dbTargets, ['id', tgt.id])
|
||||
|
||||
// -> Build update config object
|
||||
const updatedConfig = dbTarget?.config ?? {}
|
||||
if (tgt.config) {
|
||||
for (const [key, prop] of Object.entries(md.props)) {
|
||||
if (prop.readOnly) { continue }
|
||||
if (!Object.prototype.hasOwnProperty.call(tgt.config, key)) { continue }
|
||||
if (prop.sensitive && tgt.config[key] === '********') { continue }
|
||||
updatedConfig[key] = tgt.config[key]
|
||||
}
|
||||
}).where('key', tgt.key)
|
||||
}
|
||||
|
||||
// -> Target doesn't exist yet in the DB, let's create it
|
||||
if (!dbTarget) {
|
||||
WIKI.logger.debug(`No existing DB configuration for module ${tgt.module}. Creating a new one...`)
|
||||
await WIKI.models.storage.query().insert({
|
||||
id: tgt.id,
|
||||
module: tgt.module,
|
||||
siteId: args.siteId,
|
||||
isEnabled: tgt.isEnabled ?? false,
|
||||
contentTypes: {
|
||||
activeTypes: tgt.contentTypes ?? md.contentTypes.defaultTypesEnabled ?? [],
|
||||
largeThreshold: tgt.largeThreshold ?? md.contentTypes.defaultLargeThreshold ?? '5MB'
|
||||
},
|
||||
assetDelivery: {
|
||||
streaming: tgt.assetDeliveryFileStreaming ?? md?.assetDelivery?.defaultStreamingEnabled ?? false,
|
||||
directAccess: tgt.assetDeliveryDirectAccess ?? md?.assetDelivery?.defaultDirectAccessEnabled ?? false
|
||||
},
|
||||
versioning: {
|
||||
enabled: tgt.useVersioning ?? md?.versioning?.defaultEnabled ?? false
|
||||
},
|
||||
state: {
|
||||
current: 'ok'
|
||||
},
|
||||
config: updatedConfig
|
||||
})
|
||||
} else {
|
||||
WIKI.logger.debug(`Updating DB configuration for module ${tgt.module}...`)
|
||||
await WIKI.models.storage.query().patch({
|
||||
isEnabled: tgt.isEnabled ?? dbTarget.isEnabled ?? false,
|
||||
contentTypes: {
|
||||
activeTypes: tgt.contentTypes ?? dbTarget?.contentTypes?.activeTypes ?? [],
|
||||
largeThreshold: tgt.largeThreshold ?? dbTarget?.contentTypes?.largeThreshold ?? '5MB'
|
||||
},
|
||||
assetDelivery: {
|
||||
streaming: tgt.assetDeliveryFileStreaming ?? dbTarget?.assetDelivery?.streaming ?? false,
|
||||
directAccess: tgt.assetDeliveryDirectAccess ?? dbTarget?.assetDelivery?.directAccess ?? false
|
||||
},
|
||||
versioning: {
|
||||
enabled: tgt.useVersioning ?? dbTarget?.versioning?.enabled ?? false
|
||||
},
|
||||
config: updatedConfig
|
||||
}).where('id', tgt.id)
|
||||
}
|
||||
}
|
||||
await WIKI.models.storage.initTargets()
|
||||
// await WIKI.models.storage.initTargets()
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('Storage targets updated successfully')
|
||||
status: graphHelper.generateSuccess('Storage targets updated successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async executeAction(obj, args, context) {
|
||||
async setupStorageTarget (obj, args, context) {
|
||||
try {
|
||||
const tgt = await WIKI.models.storage.query().findById(args.targetId)
|
||||
if (!tgt) {
|
||||
throw new Error('Not storage target matching this ID')
|
||||
}
|
||||
const md = _.find(WIKI.storage.defs, ['key', tgt.module])
|
||||
if (!md) {
|
||||
throw new Error('No matching storage module installed.')
|
||||
}
|
||||
if (!await WIKI.models.storage.ensureModule(md.key)) {
|
||||
throw new Error('Failed to load storage module. Check logs for details.')
|
||||
}
|
||||
const result = await WIKI.storage.modules[md.key].setup(args.targetId, args.state)
|
||||
|
||||
return {
|
||||
status: graphHelper.generateSuccess('Storage target setup step succeeded'),
|
||||
state: result
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async destroyStorageTargetSetup (obj, args, context) {
|
||||
try {
|
||||
const tgt = await WIKI.models.storage.query().findById(args.targetId)
|
||||
if (!tgt) {
|
||||
throw new Error('Not storage target matching this ID')
|
||||
}
|
||||
const md = _.find(WIKI.storage.defs, ['key', tgt.module])
|
||||
if (!md) {
|
||||
throw new Error('No matching storage module installed.')
|
||||
}
|
||||
if (!await WIKI.models.storage.ensureModule(md.key)) {
|
||||
throw new Error('Failed to load storage module. Check logs for details.')
|
||||
}
|
||||
await WIKI.storage.modules[md.key].setupDestroy(args.targetId)
|
||||
|
||||
return {
|
||||
status: graphHelper.generateSuccess('Storage target setup configuration destroyed succesfully.')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async executeStorageAction (obj, args, context) {
|
||||
try {
|
||||
await WIKI.models.storage.executeAction(args.targetKey, args.handler)
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('Action completed.')
|
||||
status: graphHelper.generateSuccess('Action completed.')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
|
||||
@@ -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 info () { return {} },
|
||||
async extensions () {
|
||||
const exts = Object.values(WIKI.extensions.ext).map(ext => _.pick(ext, ['key', 'title', 'description', 'isInstalled']))
|
||||
for (let 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 (const ext of exts) {
|
||||
ext.isCompatible = await WIKI.extensions.ext[ext.key].isCompatible()
|
||||
}
|
||||
return exts
|
||||
},
|
||||
systemSecurity () {
|
||||
return WIKI.config.security
|
||||
}
|
||||
},
|
||||
SystemMutation: {
|
||||
async updateFlags (obj, args, context) {
|
||||
Mutation: {
|
||||
async updateSystemFlags (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')
|
||||
status: 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)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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 {
|
||||
responseResult: 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')
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('SSL Certificate renewed successfully.')
|
||||
}
|
||||
await WIKI.extensions.ext[args.key].install()
|
||||
// TODO: broadcast ext install
|
||||
return {
|
||||
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
|
||||
}
|
||||
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
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
module.exports = {
|
||||
// Query: {
|
||||
// tags(obj, args, context, info) {
|
||||
// return WIKI.models.Tag.findAll({ where: args })
|
||||
// }
|
||||
// },
|
||||
// Mutation: {
|
||||
// assignTagToDocument(obj, args) {
|
||||
// return WIKI.models.Tag.findById(args.tagId).then(tag => {
|
||||
// if (!tag) {
|
||||
// throw new gql.GraphQLError('Invalid Tag ID')
|
||||
// }
|
||||
// return WIKI.models.Document.findById(args.documentId).then(doc => {
|
||||
// if (!doc) {
|
||||
// throw new gql.GraphQLError('Invalid Document ID')
|
||||
// }
|
||||
// return tag.addDocument(doc)
|
||||
// })
|
||||
// })
|
||||
// },
|
||||
// createTag(obj, args) {
|
||||
// return WIKI.models.Tag.create(args)
|
||||
// },
|
||||
// deleteTag(obj, args) {
|
||||
// return WIKI.models.Tag.destroy({
|
||||
// where: {
|
||||
// id: args.id
|
||||
// },
|
||||
// limit: 1
|
||||
// })
|
||||
// },
|
||||
// removeTagFromDocument(obj, args) {
|
||||
// return WIKI.models.Tag.findById(args.tagId).then(tag => {
|
||||
// if (!tag) {
|
||||
// throw new gql.GraphQLError('Invalid Tag ID')
|
||||
// }
|
||||
// return WIKI.models.Document.findById(args.documentId).then(doc => {
|
||||
// if (!doc) {
|
||||
// throw new gql.GraphQLError('Invalid Document ID')
|
||||
// }
|
||||
// return tag.removeDocument(doc)
|
||||
// })
|
||||
// })
|
||||
// },
|
||||
// renameTag(obj, args) {
|
||||
// return WIKI.models.Group.update({
|
||||
// key: args.key
|
||||
// }, {
|
||||
// where: { id: args.id }
|
||||
// })
|
||||
// }
|
||||
// },
|
||||
// Tag: {
|
||||
// documents(tag) {
|
||||
// return tag.getDocuments()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
const graphHelper = require('../../helpers/graph')
|
||||
const _ = require('lodash')
|
||||
const CleanCSS = require('clean-css')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async theming() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async theming() { return {} }
|
||||
},
|
||||
ThemingQuery: {
|
||||
async themes(obj, args, context, info) {
|
||||
return [{ // TODO
|
||||
key: 'default',
|
||||
title: 'Default',
|
||||
author: 'requarks.io'
|
||||
}]
|
||||
},
|
||||
async config(obj, args, context, info) {
|
||||
return {
|
||||
theme: WIKI.config.theming.theme,
|
||||
iconset: WIKI.config.theming.iconset,
|
||||
darkMode: WIKI.config.theming.darkMode,
|
||||
injectCSS: new CleanCSS({ format: 'beautify' }).minify(WIKI.config.theming.injectCSS).styles,
|
||||
injectHead: WIKI.config.theming.injectHead,
|
||||
injectBody: WIKI.config.theming.injectBody
|
||||
}
|
||||
}
|
||||
},
|
||||
ThemingMutation: {
|
||||
async setConfig(obj, args, context, info) {
|
||||
try {
|
||||
if (!_.isEmpty(args.injectCSS)) {
|
||||
args.injectCSS = new CleanCSS({
|
||||
inline: false
|
||||
}).minify(args.injectCSS).styles
|
||||
}
|
||||
|
||||
WIKI.config.theming = {
|
||||
...WIKI.config.theming,
|
||||
theme: args.theme,
|
||||
iconset: args.iconset,
|
||||
darkMode: args.darkMode,
|
||||
injectCSS: args.injectCSS || '',
|
||||
injectHead: args.injectHead || '',
|
||||
injectBody: args.injectBody || ''
|
||||
}
|
||||
|
||||
await WIKI.configSvc.saveToDb(['theming'])
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('Theme config updated')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,32 +5,54 @@ const _ = require('lodash')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async users() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async users() { return {} }
|
||||
},
|
||||
UserQuery: {
|
||||
async list(obj, args, context, info) {
|
||||
return WIKI.models.users.query()
|
||||
.select('id', 'email', 'name', 'providerKey', 'isSystem', 'isActive', 'createdAt', 'lastLoginAt')
|
||||
},
|
||||
async search(obj, args, context, info) {
|
||||
return WIKI.models.users.query()
|
||||
.where('email', 'like', `%${args.query}%`)
|
||||
.orWhere('name', 'like', `%${args.query}%`)
|
||||
.limit(10)
|
||||
.select('id', 'email', 'name', 'providerKey', 'createdAt')
|
||||
},
|
||||
async single(obj, args, context, info) {
|
||||
let usr = await WIKI.models.users.query().findById(args.id)
|
||||
usr.password = ''
|
||||
usr.tfaSecret = ''
|
||||
/**
|
||||
* FETCH ALL USERS
|
||||
*/
|
||||
async users (obj, args, context, info) {
|
||||
// -> Sanitize limit
|
||||
let limit = args.pageSize ?? 20
|
||||
if (limit < 1 || limit > 1000) {
|
||||
limit = 1000
|
||||
}
|
||||
|
||||
const str = _.get(WIKI.auth.strategies, usr.providerKey)
|
||||
str.strategy = _.find(WIKI.data.authentication, ['key', str.strategyKey])
|
||||
usr.providerName = str.displayName
|
||||
usr.providerIs2FACapable = _.get(str, 'strategy.useForm', false)
|
||||
// -> Sanitize offset
|
||||
let offset = args.page ?? 1
|
||||
if (offset < 1) {
|
||||
offset = 1
|
||||
}
|
||||
|
||||
// -> Fetch Users
|
||||
return WIKI.models.users.query()
|
||||
.select('id', 'email', 'name', 'isSystem', 'isActive', 'createdAt', 'lastLoginAt')
|
||||
.where(builder => {
|
||||
if (args.filter) {
|
||||
builder.where('email', 'like', `%${args.filter}%`)
|
||||
.orWhere('name', 'like', `%${args.filter}%`)
|
||||
}
|
||||
})
|
||||
.orderBy(args.orderBy ?? 'name', args.orderByDirection ?? 'asc')
|
||||
.offset((offset - 1) * limit)
|
||||
.limit(limit)
|
||||
},
|
||||
/**
|
||||
* FETCH A SINGLE USER
|
||||
*/
|
||||
async userById (obj, args, context, info) {
|
||||
const usr = await WIKI.models.users.query().findById(args.id)
|
||||
|
||||
// const str = _.get(WIKI.auth.strategies, usr.providerKey)
|
||||
// str.strategy = _.find(WIKI.data.authentication, ['key', str.strategyKey])
|
||||
// usr.providerName = str.displayName
|
||||
// usr.providerIs2FACapable = _.get(str, 'strategy.useForm', false)
|
||||
|
||||
usr.auth = _.mapValues(usr.auth, (auth, providerKey) => {
|
||||
if (auth.password) {
|
||||
auth.password = '***'
|
||||
}
|
||||
auth.module = providerKey === '00910749-8ab6-498a-9be0-f4ca28ea5e52' ? 'google' : 'local'
|
||||
auth._moduleName = providerKey === '00910749-8ab6-498a-9be0-f4ca28ea5e52' ? 'Google' : 'Local'
|
||||
return auth
|
||||
})
|
||||
|
||||
return usr
|
||||
},
|
||||
@@ -61,19 +83,19 @@ module.exports = {
|
||||
.limit(10)
|
||||
}
|
||||
},
|
||||
UserMutation: {
|
||||
async create (obj, args) {
|
||||
Mutation: {
|
||||
async createUser (obj, args) {
|
||||
try {
|
||||
await WIKI.models.users.createNewUser(args)
|
||||
await WIKI.models.users.createNewUser({ ...args, passwordRaw: args.password, isVerified: true })
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('User created successfully')
|
||||
status: graphHelper.generateSuccess('User created successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async delete (obj, args) {
|
||||
async deleteUser (obj, args) {
|
||||
try {
|
||||
if (args.id <= 2) {
|
||||
throw new WIKI.Error.UserDeleteProtected()
|
||||
@@ -84,7 +106,7 @@ module.exports = {
|
||||
WIKI.events.outbound.emit('addAuthRevoke', { id: args.id, kind: 'u' })
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('User deleted successfully')
|
||||
status: graphHelper.generateSuccess('User deleted successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.message.indexOf('foreign') >= 0) {
|
||||
@@ -94,40 +116,40 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
async update (obj, args) {
|
||||
async updateUser (obj, args) {
|
||||
try {
|
||||
await WIKI.models.users.updateUser(args)
|
||||
await WIKI.models.users.updateUser(args.id, args.patch)
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('User created successfully')
|
||||
status: graphHelper.generateSuccess('User updated successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async verify (obj, args) {
|
||||
async verifyUser (obj, args) {
|
||||
try {
|
||||
await WIKI.models.users.query().patch({ isVerified: true }).findById(args.id)
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('User verified successfully')
|
||||
status: graphHelper.generateSuccess('User verified successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async activate (obj, args) {
|
||||
async activateUser (obj, args) {
|
||||
try {
|
||||
await WIKI.models.users.query().patch({ isActive: true }).findById(args.id)
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('User activated successfully')
|
||||
status: graphHelper.generateSuccess('User activated successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async deactivate (obj, args) {
|
||||
async deactivateUser (obj, args) {
|
||||
try {
|
||||
if (args.id <= 2) {
|
||||
throw new Error('Cannot deactivate system accounts.')
|
||||
@@ -138,35 +160,35 @@ module.exports = {
|
||||
WIKI.events.outbound.emit('addAuthRevoke', { id: args.id, kind: 'u' })
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('User deactivated successfully')
|
||||
status: graphHelper.generateSuccess('User deactivated successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async enableTFA (obj, args) {
|
||||
async enableUserTFA (obj, args) {
|
||||
try {
|
||||
await WIKI.models.users.query().patch({ tfaIsActive: true, tfaSecret: null }).findById(args.id)
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('User 2FA enabled successfully')
|
||||
status: graphHelper.generateSuccess('User 2FA enabled successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
async disableTFA (obj, args) {
|
||||
async disableUserTFA (obj, args) {
|
||||
try {
|
||||
await WIKI.models.users.query().patch({ tfaIsActive: false, tfaSecret: null }).findById(args.id)
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('User 2FA disabled successfully')
|
||||
status: graphHelper.generateSuccess('User 2FA disabled successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
resetPassword (obj, args) {
|
||||
resetUserPassword (obj, args) {
|
||||
return false
|
||||
},
|
||||
async updateProfile (obj, args, context) {
|
||||
@@ -203,7 +225,7 @@ module.exports = {
|
||||
const newToken = await WIKI.models.users.refreshToken(usr.id)
|
||||
|
||||
return {
|
||||
responseResult: graphHelper.generateSuccess('User profile updated successfully'),
|
||||
status: graphHelper.generateSuccess('User profile updated successfully'),
|
||||
jwt: newToken.token
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
|
||||
const gql = require('graphql')
|
||||
|
||||
module.exports = {
|
||||
Date: new gql.GraphQLScalarType({
|
||||
name: 'Date',
|
||||
description: 'ISO date-time string at UTC',
|
||||
parseValue(value) {
|
||||
return new Date(value)
|
||||
},
|
||||
serialize(value) {
|
||||
return value.toISOString()
|
||||
},
|
||||
parseLiteral(ast) {
|
||||
if (ast.kind !== gql.Kind.STRING) {
|
||||
throw new TypeError('Date value must be an string!')
|
||||
}
|
||||
return new Date(ast.value)
|
||||
module.exports = new gql.GraphQLScalarType({
|
||||
name: 'Date',
|
||||
description: 'ISO date-time string at UTC',
|
||||
parseValue(value) {
|
||||
return new Date(value)
|
||||
},
|
||||
serialize(value) {
|
||||
return value.toISOString()
|
||||
},
|
||||
parseLiteral(ast) {
|
||||
if (ast.kind !== gql.Kind.STRING) {
|
||||
throw new TypeError('Date value must be an string!')
|
||||
}
|
||||
})
|
||||
}
|
||||
return new Date(ast.value)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -39,21 +39,19 @@ function parseObject (typeName, ast, variables) {
|
||||
return value
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
JSON: new GraphQLScalarType({
|
||||
name: 'JSON',
|
||||
description:
|
||||
'The `JSON` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).',
|
||||
specifiedByUrl:
|
||||
'http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf',
|
||||
serialize: ensureObject,
|
||||
parseValue: ensureObject,
|
||||
parseLiteral: (ast, variables) => {
|
||||
if (ast.kind !== Kind.OBJECT) {
|
||||
throw new TypeError(`JSONObject cannot represent non-object value: ${ast}`)
|
||||
}
|
||||
|
||||
return parseObject('JSONObject', ast, variables)
|
||||
module.exports = new GraphQLScalarType({
|
||||
name: 'JSON',
|
||||
description:
|
||||
'The `JSON` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).',
|
||||
specifiedByUrl:
|
||||
'http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf',
|
||||
serialize: ensureObject,
|
||||
parseValue: ensureObject,
|
||||
parseLiteral: (ast, variables) => {
|
||||
if (ast.kind !== Kind.OBJECT) {
|
||||
throw new TypeError(`JSONObject cannot represent non-object value: ${ast}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return parseObject('JSONObject', ast, variables)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const { Kind, GraphQLScalarType } = require('graphql')
|
||||
// const { Kind } = require('graphql/language')
|
||||
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
||||
const nilUUID = '00000000-0000-0000-0000-000000000000'
|
||||
@@ -8,32 +7,30 @@ function isUUID (value) {
|
||||
return uuidRegex.test(value) || nilUUID === value
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
UUID: new GraphQLScalarType({
|
||||
name: 'UUID',
|
||||
description: 'The `UUID` scalar type represents UUID values as specified by [RFC 4122](https://tools.ietf.org/html/rfc4122).',
|
||||
serialize: (value) => {
|
||||
if (!isUUID(value)) {
|
||||
throw new TypeError(`UUID cannot represent non-UUID value: ${value}`)
|
||||
}
|
||||
|
||||
return value.toLowerCase()
|
||||
},
|
||||
parseValue: (value) => {
|
||||
if (!isUUID(value)) {
|
||||
throw new TypeError(`UUID cannot represent non-UUID value: ${value}`)
|
||||
}
|
||||
|
||||
return value.toLowerCase()
|
||||
},
|
||||
parseLiteral: (ast) => {
|
||||
if (ast.kind === Kind.STRING) {
|
||||
if (isUUID(ast.value)) {
|
||||
return ast.value
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
module.exports = new GraphQLScalarType({
|
||||
name: 'UUID',
|
||||
description: 'The `UUID` scalar type represents UUID values as specified by [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122).',
|
||||
serialize: (value) => {
|
||||
if (!isUUID(value)) {
|
||||
throw new TypeError(`UUID cannot represent non-UUID value: ${value}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return value.toLowerCase()
|
||||
},
|
||||
parseValue: (value) => {
|
||||
if (!isUUID(value)) {
|
||||
throw new TypeError(`UUID cannot represent non-UUID value: ${value}`)
|
||||
}
|
||||
|
||||
return value.toLowerCase()
|
||||
},
|
||||
parseLiteral: (ast) => {
|
||||
if (ast.kind === Kind.STRING) {
|
||||
if (isUUID(ast.value)) {
|
||||
return ast.value
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,45 +3,23 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
analytics: AnalyticsQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
analytics: AnalyticsMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
"""
|
||||
Queries for Analytics
|
||||
"""
|
||||
type AnalyticsQuery {
|
||||
"""
|
||||
Fetch list of Analytics providers and their configuration
|
||||
"""
|
||||
providers(
|
||||
analyticsProviders(
|
||||
"Return only active providers"
|
||||
isEnabled: Boolean
|
||||
): [AnalyticsProvider] @auth(requires: ["manage:system"])
|
||||
): [AnalyticsProvider]
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
"""
|
||||
Mutations for Analytics
|
||||
"""
|
||||
type AnalyticsMutation {
|
||||
extend type Mutation {
|
||||
"""
|
||||
Update a list of Analytics providers and their configuration
|
||||
"""
|
||||
updateProviders(
|
||||
updateAnalyticsProviders(
|
||||
"List of providers"
|
||||
providers: [AnalyticsProviderInput]!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
|
||||
@@ -3,49 +3,33 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
assets: AssetQuery
|
||||
assets(
|
||||
folderId: Int!
|
||||
kind: AssetKind!
|
||||
): [AssetItem]
|
||||
|
||||
assetsFolders(
|
||||
parentFolderId: Int!
|
||||
): [AssetFolder]
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
assets: AssetMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type AssetQuery {
|
||||
list(
|
||||
folderId: Int!
|
||||
kind: AssetKind!
|
||||
): [AssetItem] @auth(requires: ["manage:system", "read:assets"])
|
||||
|
||||
folders(
|
||||
parentFolderId: Int!
|
||||
): [AssetFolder] @auth(requires: ["manage:system", "read:assets"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type AssetMutation {
|
||||
createFolder(
|
||||
createAssetsFolder(
|
||||
parentFolderId: Int!
|
||||
slug: String!
|
||||
name: String
|
||||
): DefaultResponse @auth(requires: ["manage:system", "write:assets"])
|
||||
): DefaultResponse
|
||||
|
||||
renameAsset(
|
||||
id: Int!
|
||||
filename: String!
|
||||
): DefaultResponse @auth(requires: ["manage:system", "manage:assets"])
|
||||
): DefaultResponse
|
||||
|
||||
deleteAsset(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:system", "manage:assets"])
|
||||
): DefaultResponse
|
||||
|
||||
flushTempUploads: DefaultResponse @auth(requires: ["manage:system"])
|
||||
flushTempUploads: DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
|
||||
@@ -3,40 +3,22 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
authentication: AuthenticationQuery
|
||||
apiKeys: [AuthenticationApiKey]
|
||||
|
||||
apiState: Boolean
|
||||
|
||||
authStrategies(
|
||||
enabledOnly: Boolean
|
||||
): [AuthenticationStrategy]
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
authentication: AuthenticationMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type AuthenticationQuery {
|
||||
apiKeys: [AuthenticationApiKey] @auth(requires: ["manage:system", "manage:api"])
|
||||
|
||||
apiState: Boolean! @auth(requires: ["manage:system", "manage:api"])
|
||||
|
||||
strategies: [AuthenticationStrategy] @auth(requires: ["manage:system"])
|
||||
|
||||
activeStrategies(
|
||||
enabledOnly: Boolean
|
||||
): [AuthenticationActiveStrategy]
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type AuthenticationMutation {
|
||||
createApiKey(
|
||||
name: String!
|
||||
expiration: String!
|
||||
fullAccess: Boolean!
|
||||
group: Int
|
||||
): AuthenticationCreateApiKeyResponse @auth(requires: ["manage:system", "manage:api"])
|
||||
): AuthenticationCreateApiKeyResponse
|
||||
|
||||
login(
|
||||
username: String!
|
||||
@@ -67,19 +49,19 @@ type AuthenticationMutation {
|
||||
|
||||
revokeApiKey(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:system", "manage:api"])
|
||||
): DefaultResponse
|
||||
|
||||
setApiState(
|
||||
enabled: Boolean!
|
||||
): DefaultResponse @auth(requires: ["manage:system", "manage:api"])
|
||||
): DefaultResponse
|
||||
|
||||
updateStrategies(
|
||||
updateAuthStrategies(
|
||||
strategies: [AuthenticationStrategyInput]!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
regenerateCertificates: DefaultResponse @auth(requires: ["manage:system"])
|
||||
regenerateCertificates: DefaultResponse
|
||||
|
||||
resetGuestUser: DefaultResponse @auth(requires: ["manage:system"])
|
||||
resetGuestUser: DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -113,7 +95,7 @@ type AuthenticationActiveStrategy {
|
||||
}
|
||||
|
||||
type AuthenticationLoginResponse {
|
||||
responseResult: ResponseStatus
|
||||
operation: Operation
|
||||
jwt: String
|
||||
mustChangePwd: Boolean
|
||||
mustProvideTFA: Boolean
|
||||
@@ -124,7 +106,7 @@ type AuthenticationLoginResponse {
|
||||
}
|
||||
|
||||
type AuthenticationRegisterResponse {
|
||||
responseResult: ResponseStatus
|
||||
operation: Operation
|
||||
jwt: String
|
||||
}
|
||||
|
||||
@@ -151,6 +133,6 @@ type AuthenticationApiKey {
|
||||
}
|
||||
|
||||
type AuthenticationCreateApiKeyResponse {
|
||||
responseResult: ResponseStatus
|
||||
operation: Operation
|
||||
key: String
|
||||
}
|
||||
|
||||
@@ -3,55 +3,39 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
comments: CommentQuery
|
||||
commentsProviders: [CommentProvider]
|
||||
|
||||
comments(
|
||||
locale: String!
|
||||
path: String!
|
||||
): [CommentPost]!
|
||||
|
||||
commentById(
|
||||
id: Int!
|
||||
): CommentPost
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
comments: CommentMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type CommentQuery {
|
||||
providers: [CommentProvider] @auth(requires: ["manage:system"])
|
||||
|
||||
list(
|
||||
locale: String!
|
||||
path: String!
|
||||
): [CommentPost]! @auth(requires: ["read:comments", "manage:system"])
|
||||
|
||||
single(
|
||||
id: Int!
|
||||
): CommentPost @auth(requires: ["read:comments", "manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type CommentMutation {
|
||||
updateProviders(
|
||||
updateCommentsProviders(
|
||||
providers: [CommentProviderInput]
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
create(
|
||||
createComment(
|
||||
pageId: Int!
|
||||
replyTo: Int
|
||||
content: String!
|
||||
guestName: String
|
||||
guestEmail: String
|
||||
): CommentCreateResponse @auth(requires: ["write:comments", "manage:system"]) @rateLimit(limit: 1, duration: 15)
|
||||
): CommentCreateResponse @rateLimit(limit: 1, duration: 15)
|
||||
|
||||
update(
|
||||
updateComment(
|
||||
id: Int!
|
||||
content: String!
|
||||
): CommentUpdateResponse @auth(requires: ["write:comments", "manage:comments", "manage:system"])
|
||||
): CommentUpdateResponse
|
||||
|
||||
delete(
|
||||
deleteComment(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:comments", "manage:system"])
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -59,9 +43,9 @@ type CommentMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type CommentProvider {
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
title: String!
|
||||
isEnabled: Boolean
|
||||
key: String
|
||||
title: String
|
||||
description: String
|
||||
logo: String
|
||||
website: String
|
||||
@@ -76,23 +60,23 @@ input CommentProviderInput {
|
||||
}
|
||||
|
||||
type CommentPost {
|
||||
id: Int!
|
||||
content: String! @auth(requires: ["write:comments", "manage:comments", "manage:system"])
|
||||
render: String!
|
||||
authorId: Int!
|
||||
authorName: String!
|
||||
authorEmail: String! @auth(requires: ["manage:system"])
|
||||
authorIP: String! @auth(requires: ["manage:system"])
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
id: Int
|
||||
content: String
|
||||
render: String
|
||||
authorId: Int
|
||||
authorName: String
|
||||
authorEmail: String
|
||||
authorIP: String
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type CommentCreateResponse {
|
||||
responseResult: ResponseStatus
|
||||
operation: Operation
|
||||
id: Int
|
||||
}
|
||||
|
||||
type CommentUpdateResponse {
|
||||
responseResult: ResponseStatus
|
||||
operation: Operation
|
||||
render: String
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Wiki.js GraphQL Schema #
|
||||
# ====================== #
|
||||
|
||||
# DIRECTIVES
|
||||
# DIRECTIVES (deprecated)
|
||||
# ----------
|
||||
|
||||
directive @auth(requires: [String]) on QUERY | FIELD_DEFINITION | ARGUMENT_DEFINITION
|
||||
@@ -12,8 +12,8 @@ directive @auth(requires: [String]) on QUERY | FIELD_DEFINITION | ARGUMENT_DEFIN
|
||||
|
||||
# Generic Key Value Pair
|
||||
type KeyValuePair {
|
||||
key: String!
|
||||
value: String!
|
||||
key: String
|
||||
value: String
|
||||
}
|
||||
# General Key Value Pair Input
|
||||
input KeyValuePairInput {
|
||||
@@ -23,14 +23,13 @@ input KeyValuePairInput {
|
||||
|
||||
# Generic Mutation Response
|
||||
type DefaultResponse {
|
||||
responseResult: ResponseStatus
|
||||
operation: Operation
|
||||
}
|
||||
|
||||
# Mutation Status
|
||||
type ResponseStatus {
|
||||
succeeded: Boolean!
|
||||
errorCode: Int!
|
||||
slug: String!
|
||||
# Mutation Operation
|
||||
type Operation {
|
||||
succeeded: Boolean
|
||||
slug: String
|
||||
message: String
|
||||
}
|
||||
|
||||
@@ -47,6 +46,3 @@ type Query
|
||||
|
||||
# Mutations (Create, Update, Delete)
|
||||
type Mutation
|
||||
|
||||
# Subscriptions (Push, Real-time)
|
||||
type Subscription
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# ===============================================
|
||||
# CONTRIBUTE
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
contribute: ContributeQuery
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type ContributeQuery {
|
||||
contributors: [ContributeContributor]
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# TYPES
|
||||
# -----------------------------------------------
|
||||
|
||||
type ContributeContributor {
|
||||
id: String!
|
||||
source: String!
|
||||
name: String!
|
||||
joined: Date!
|
||||
website: String
|
||||
twitter: String
|
||||
avatar: String
|
||||
}
|
||||
@@ -3,58 +3,39 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
groups: GroupQuery
|
||||
groups(
|
||||
filter: String
|
||||
orderBy: String
|
||||
): [Group]
|
||||
|
||||
groupById(
|
||||
id: Int!
|
||||
): Group
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
groups: GroupMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type GroupQuery {
|
||||
list(
|
||||
filter: String
|
||||
orderBy: String
|
||||
): [GroupMinimal] @auth(requires: ["write:groups", "manage:groups", "manage:system"])
|
||||
|
||||
single(
|
||||
id: Int!
|
||||
): Group @auth(requires: ["write:groups", "manage:groups", "manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type GroupMutation {
|
||||
create(
|
||||
createGroup(
|
||||
name: String!
|
||||
): GroupResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
|
||||
): GroupResponse
|
||||
|
||||
update(
|
||||
updateGroup(
|
||||
id: Int!
|
||||
name: String!
|
||||
redirectOnLogin: String!
|
||||
permissions: [String]!
|
||||
pageRules: [PageRuleInput]!
|
||||
): DefaultResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
|
||||
patch: GroupUpdateInput!
|
||||
): DefaultResponse
|
||||
|
||||
delete(
|
||||
deleteGroup(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
assignUser(
|
||||
assignUserToGroup(
|
||||
groupId: Int!
|
||||
userId: Int!
|
||||
): DefaultResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
unassignUser(
|
||||
unassignUserFromGroup(
|
||||
groupId: Int!
|
||||
userId: Int!
|
||||
): DefaultResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -62,38 +43,36 @@ type GroupMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type GroupResponse {
|
||||
responseResult: ResponseStatus!
|
||||
operation: Operation
|
||||
group: Group
|
||||
}
|
||||
|
||||
type GroupMinimal {
|
||||
id: Int!
|
||||
name: String!
|
||||
isSystem: Boolean!
|
||||
userCount: Int
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
}
|
||||
|
||||
type Group {
|
||||
id: Int!
|
||||
name: String!
|
||||
isSystem: Boolean!
|
||||
id: Int
|
||||
name: String
|
||||
isSystem: Boolean
|
||||
redirectOnLogin: String
|
||||
permissions: [String]!
|
||||
permissions: [String]
|
||||
pageRules: [PageRule]
|
||||
users: [UserMinimal]
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type PageRule {
|
||||
id: String!
|
||||
deny: Boolean!
|
||||
match: PageRuleMatch!
|
||||
roles: [String]!
|
||||
path: String!
|
||||
locales: [String]!
|
||||
id: String
|
||||
deny: Boolean
|
||||
match: PageRuleMatch
|
||||
roles: [String]
|
||||
path: String
|
||||
locales: [String]
|
||||
}
|
||||
|
||||
input GroupUpdateInput {
|
||||
name: String!
|
||||
redirectOnLogin: String!
|
||||
permissions: [String]!
|
||||
pageRules: [PageRuleInput]!
|
||||
}
|
||||
|
||||
input PageRuleInput {
|
||||
|
||||
@@ -3,38 +3,21 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
localization: LocalizationQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
localization: LocalizationMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type LocalizationQuery {
|
||||
locales: [LocalizationLocale]
|
||||
config: LocalizationConfig
|
||||
translations(locale: String!, namespace: String!): [Translation]
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type LocalizationMutation {
|
||||
extend type Mutation {
|
||||
downloadLocale(
|
||||
locale: String!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
updateLocale(
|
||||
locale: String!
|
||||
autoUpdate: Boolean!
|
||||
namespacing: Boolean!
|
||||
namespaces: [String]!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -42,25 +25,18 @@ type LocalizationMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type LocalizationLocale {
|
||||
availability: Int!
|
||||
code: String!
|
||||
createdAt: Date!
|
||||
availability: Int
|
||||
code: String
|
||||
createdAt: Date
|
||||
installDate: Date
|
||||
isInstalled: Boolean!
|
||||
isRTL: Boolean!
|
||||
name: String!
|
||||
nativeName: String!
|
||||
updatedAt: Date!
|
||||
}
|
||||
|
||||
type LocalizationConfig {
|
||||
locale: String!
|
||||
autoUpdate: Boolean!
|
||||
namespacing: Boolean!
|
||||
namespaces: [String]!
|
||||
isInstalled: Boolean
|
||||
isRTL: Boolean
|
||||
name: String
|
||||
nativeName: String
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type Translation {
|
||||
key: String!
|
||||
value: String!
|
||||
key: String
|
||||
value: String
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
# ===============================================
|
||||
# LOGGING
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
logging: LoggingQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
logging: LoggingMutation
|
||||
}
|
||||
|
||||
extend type Subscription {
|
||||
loggingLiveTrail: LoggerTrailLine
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type LoggingQuery {
|
||||
loggers(
|
||||
filter: String
|
||||
orderBy: String
|
||||
): [Logger] @auth(requires: ["manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type LoggingMutation {
|
||||
updateLoggers(
|
||||
loggers: [LoggerInput]
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# TYPES
|
||||
# -----------------------------------------------
|
||||
|
||||
type Logger {
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
title: String!
|
||||
description: String
|
||||
logo: String
|
||||
website: String
|
||||
level: String
|
||||
config: [KeyValuePair]
|
||||
}
|
||||
|
||||
input LoggerInput {
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
level: String!
|
||||
config: [KeyValuePairInput]
|
||||
}
|
||||
|
||||
type LoggerTrailLine {
|
||||
level: String!
|
||||
output: String!
|
||||
timestamp: Date!
|
||||
}
|
||||
@@ -3,31 +3,15 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
mail: MailQuery
|
||||
mailConfig: MailConfig
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
mail: MailMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type MailQuery {
|
||||
config: MailConfig @auth(requires: ["manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type MailMutation {
|
||||
sendTest(
|
||||
sendMailTest(
|
||||
recipientEmail: String!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
updateConfig(
|
||||
updateMailConfig(
|
||||
senderName: String!
|
||||
senderEmail: String!
|
||||
host: String!
|
||||
@@ -40,7 +24,7 @@ type MailMutation {
|
||||
dkimDomainName: String!
|
||||
dkimKeySelector: String!
|
||||
dkimPrivateKey: String!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -48,16 +32,16 @@ type MailMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type MailConfig {
|
||||
senderName: String!
|
||||
senderEmail: String!
|
||||
host: String!
|
||||
port: Int!
|
||||
secure: Boolean!
|
||||
verifySSL: Boolean!
|
||||
user: String!
|
||||
pass: String!
|
||||
useDKIM: Boolean!
|
||||
dkimDomainName: String!
|
||||
dkimKeySelector: String!
|
||||
dkimPrivateKey: String!
|
||||
senderName: String
|
||||
senderEmail: String
|
||||
host: String
|
||||
port: Int
|
||||
secure: Boolean
|
||||
verifySSL: Boolean
|
||||
user: String
|
||||
pass: String
|
||||
useDKIM: Boolean
|
||||
dkimDomainName: String
|
||||
dkimKeySelector: String
|
||||
dkimPrivateKey: String
|
||||
}
|
||||
|
||||
@@ -3,33 +3,21 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
navigation: NavigationQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
navigation: NavigationMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type NavigationQuery {
|
||||
tree: [NavigationTree]! @auth(requires: ["manage:navigation", "manage:system"])
|
||||
config: NavigationConfig! @auth(requires: ["manage:navigation", "manage:system"])
|
||||
navigationTree: [NavigationTree]
|
||||
navigationConfig: NavigationConfig
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type NavigationMutation {
|
||||
updateTree(
|
||||
extend type Mutation {
|
||||
updateNavigationTree(
|
||||
tree: [NavigationTreeInput]!
|
||||
): DefaultResponse @auth(requires: ["manage:navigation", "manage:system"])
|
||||
updateConfig(
|
||||
): DefaultResponse
|
||||
updateNavigationConfig(
|
||||
mode: NavigationMode!
|
||||
): DefaultResponse @auth(requires: ["manage:navigation", "manage:system"])
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -37,8 +25,8 @@ type NavigationMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type NavigationTree {
|
||||
locale: String!
|
||||
items: [NavigationItem]!
|
||||
locale: String
|
||||
items: [NavigationItem]
|
||||
}
|
||||
|
||||
input NavigationTreeInput {
|
||||
@@ -47,8 +35,8 @@ input NavigationTreeInput {
|
||||
}
|
||||
|
||||
type NavigationItem {
|
||||
id: String!
|
||||
kind: String!
|
||||
id: String
|
||||
kind: String
|
||||
label: String
|
||||
icon: String
|
||||
targetType: String
|
||||
@@ -69,7 +57,7 @@ input NavigationItemInput {
|
||||
}
|
||||
|
||||
type NavigationConfig {
|
||||
mode: NavigationMode!
|
||||
mode: NavigationMode
|
||||
}
|
||||
|
||||
enum NavigationMode {
|
||||
|
||||
+133
-149
@@ -3,36 +3,24 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
pages: PageQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
pages: PageMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type PageQuery {
|
||||
history(
|
||||
pageHistoryById(
|
||||
id: Int!
|
||||
offsetPage: Int
|
||||
offsetSize: Int
|
||||
): PageHistoryResult @auth(requires: ["manage:system", "read:history"])
|
||||
): PageHistoryResult
|
||||
|
||||
version(
|
||||
pageVersionById(
|
||||
pageId: Int!
|
||||
versionId: Int!
|
||||
): PageVersion @auth(requires: ["manage:system", "read:history"])
|
||||
): PageVersion
|
||||
|
||||
search(
|
||||
searchPages(
|
||||
query: String!
|
||||
path: String
|
||||
locale: String
|
||||
): PageSearchResponse! @auth(requires: ["manage:system", "read:pages"])
|
||||
): PageSearchResponse!
|
||||
|
||||
list(
|
||||
pages(
|
||||
limit: Int
|
||||
orderBy: PageOrderBy
|
||||
orderByDirection: PageOrderByDirection
|
||||
@@ -40,46 +28,42 @@ type PageQuery {
|
||||
locale: String
|
||||
creatorId: Int
|
||||
authorId: Int
|
||||
): [PageListItem!]! @auth(requires: ["manage:system", "read:pages"])
|
||||
): [PageListItem!]!
|
||||
|
||||
single(
|
||||
pageById(
|
||||
id: Int!
|
||||
): Page @auth(requires: ["read:pages", "manage:system"])
|
||||
): Page
|
||||
|
||||
tags: [PageTag]! @auth(requires: ["manage:system", "read:pages"])
|
||||
tags: [PageTag]!
|
||||
|
||||
searchTags(
|
||||
query: String!
|
||||
): [String]! @auth(requires: ["manage:system", "read:pages"])
|
||||
): [String]!
|
||||
|
||||
tree(
|
||||
pageTree(
|
||||
path: String
|
||||
parent: Int
|
||||
mode: PageTreeMode!
|
||||
locale: String!
|
||||
includeAncestors: Boolean
|
||||
): [PageTreeItem] @auth(requires: ["manage:system", "read:pages"])
|
||||
): [PageTreeItem]
|
||||
|
||||
links(
|
||||
pageLinks(
|
||||
locale: String!
|
||||
): [PageLinkItem] @auth(requires: ["manage:system", "read:pages"])
|
||||
): [PageLinkItem]
|
||||
|
||||
checkConflicts(
|
||||
id: Int!
|
||||
checkoutDate: Date!
|
||||
): Boolean! @auth(requires: ["write:pages", "manage:pages", "manage:system"])
|
||||
): Boolean!
|
||||
|
||||
conflictLatest(
|
||||
checkConflictsLatest(
|
||||
id: Int!
|
||||
): PageConflictLatest! @auth(requires: ["write:pages", "manage:pages", "manage:system"])
|
||||
): PageConflictLatest!
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type PageMutation {
|
||||
create(
|
||||
extend type Mutation {
|
||||
createPage(
|
||||
content: String!
|
||||
description: String!
|
||||
editor: String!
|
||||
@@ -93,9 +77,9 @@ type PageMutation {
|
||||
scriptJs: String
|
||||
tags: [String]!
|
||||
title: String!
|
||||
): PageResponse @auth(requires: ["write:pages", "manage:pages", "manage:system"])
|
||||
): PageResponse
|
||||
|
||||
update(
|
||||
updatePage(
|
||||
id: Int!
|
||||
content: String
|
||||
description: String
|
||||
@@ -110,54 +94,54 @@ type PageMutation {
|
||||
scriptJs: String
|
||||
tags: [String]
|
||||
title: String
|
||||
): PageResponse @auth(requires: ["write:pages", "manage:pages", "manage:system"])
|
||||
): PageResponse
|
||||
|
||||
convert(
|
||||
convertPage(
|
||||
id: Int!
|
||||
editor: String!
|
||||
): DefaultResponse @auth(requires: ["write:pages", "manage:pages", "manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
move(
|
||||
renamePage(
|
||||
id: Int!
|
||||
destinationPath: String!
|
||||
destinationLocale: String!
|
||||
): DefaultResponse @auth(requires: ["manage:pages", "manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
delete(
|
||||
deletePage(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["delete:pages", "manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
deleteTag(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
updateTag(
|
||||
id: Int!
|
||||
tag: String!
|
||||
title: String!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
flushCache: DefaultResponse @auth(requires: ["manage:system"])
|
||||
flushCache: DefaultResponse
|
||||
|
||||
migrateToLocale(
|
||||
sourceLocale: String!
|
||||
targetLocale: String!
|
||||
): PageMigrationResponse @auth(requires: ["manage:system"])
|
||||
): PageMigrationResponse
|
||||
|
||||
rebuildTree: DefaultResponse @auth(requires: ["manage:system"])
|
||||
rebuildPageTree: DefaultResponse
|
||||
|
||||
render(
|
||||
renderPage(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
restore(
|
||||
restorePage(
|
||||
pageId: Int!
|
||||
versionId: Int!
|
||||
): DefaultResponse @auth(requires: ["write:pages", "manage:pages", "manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
purgeHistory (
|
||||
purgePagesHistory (
|
||||
olderThan: String!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -165,152 +149,152 @@ type PageMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type PageResponse {
|
||||
responseResult: ResponseStatus!
|
||||
operation: Operation
|
||||
page: Page
|
||||
}
|
||||
|
||||
type PageMigrationResponse {
|
||||
responseResult: ResponseStatus!
|
||||
operation: Operation
|
||||
count: Int
|
||||
}
|
||||
|
||||
type Page {
|
||||
id: Int!
|
||||
path: String!
|
||||
hash: String!
|
||||
title: String!
|
||||
description: String!
|
||||
isPrivate: Boolean! @auth(requires: ["write:pages", "manage:system"])
|
||||
isPublished: Boolean! @auth(requires: ["write:pages", "manage:system"])
|
||||
privateNS: String @auth(requires: ["write:pages", "manage:system"])
|
||||
publishStartDate: Date! @auth(requires: ["write:pages", "manage:system"])
|
||||
publishEndDate: Date! @auth(requires: ["write:pages", "manage:system"])
|
||||
tags: [PageTag]!
|
||||
content: String! @auth(requires: ["read:source", "write:pages", "manage:system"])
|
||||
id: Int
|
||||
path: String
|
||||
hash: String
|
||||
title: String
|
||||
description: String
|
||||
isPrivate: Boolean
|
||||
isPublished: Boolean
|
||||
privateNS: String
|
||||
publishStartDate: Date
|
||||
publishEndDate: Date
|
||||
tags: [PageTag]
|
||||
content: String
|
||||
render: String
|
||||
toc: String
|
||||
contentType: String!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
editor: String! @auth(requires: ["write:pages", "manage:system"])
|
||||
locale: String!
|
||||
contentType: String
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
editor: String
|
||||
locale: String
|
||||
scriptCss: String
|
||||
scriptJs: String
|
||||
authorId: Int! @auth(requires: ["write:pages", "manage:system"])
|
||||
authorName: String! @auth(requires: ["write:pages", "manage:system"])
|
||||
authorEmail: String! @auth(requires: ["write:pages", "manage:system"])
|
||||
creatorId: Int! @auth(requires: ["write:pages", "manage:system"])
|
||||
creatorName: String! @auth(requires: ["write:pages", "manage:system"])
|
||||
creatorEmail: String! @auth(requires: ["write:pages", "manage:system"])
|
||||
authorId: Int
|
||||
authorName: String
|
||||
authorEmail: String
|
||||
creatorId: Int
|
||||
creatorName: String
|
||||
creatorEmail: String
|
||||
}
|
||||
|
||||
type PageTag {
|
||||
id: Int!
|
||||
tag: String!
|
||||
id: Int
|
||||
tag: String
|
||||
title: String
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
type PageHistory {
|
||||
versionId: Int!
|
||||
versionDate: Date!
|
||||
authorId: Int!
|
||||
authorName: String!
|
||||
actionType: String!
|
||||
versionId: Int
|
||||
versionDate: Date
|
||||
authorId: Int
|
||||
authorName: String
|
||||
actionType: String
|
||||
valueBefore: String
|
||||
valueAfter: String
|
||||
}
|
||||
|
||||
type PageVersion {
|
||||
action: String!
|
||||
authorId: String!
|
||||
authorName: String!
|
||||
content: String!
|
||||
contentType: String!
|
||||
createdAt: Date!
|
||||
versionDate: Date!
|
||||
description: String!
|
||||
editor: String!
|
||||
isPrivate: Boolean!
|
||||
isPublished: Boolean!
|
||||
locale: String!
|
||||
pageId: Int!
|
||||
path: String!
|
||||
publishEndDate: Date!
|
||||
publishStartDate: Date!
|
||||
tags: [String]!
|
||||
title: String!
|
||||
versionId: Int!
|
||||
action: String
|
||||
authorId: String
|
||||
authorName: String
|
||||
content: String
|
||||
contentType: String
|
||||
createdAt: Date
|
||||
versionDate: Date
|
||||
description: String
|
||||
editor: String
|
||||
isPrivate: Boolean
|
||||
isPublished: Boolean
|
||||
locale: String
|
||||
pageId: Int
|
||||
path: String
|
||||
publishEndDate: Date
|
||||
publishStartDate: Date
|
||||
tags: [String]
|
||||
title: String
|
||||
versionId: Int
|
||||
}
|
||||
|
||||
type PageHistoryResult {
|
||||
trail: [PageHistory]
|
||||
total: Int!
|
||||
total: Int
|
||||
}
|
||||
|
||||
type PageSearchResponse {
|
||||
results: [PageSearchResult]!
|
||||
suggestions: [String]!
|
||||
totalHits: Int!
|
||||
results: [PageSearchResult]
|
||||
suggestions: [String]
|
||||
totalHits: Int
|
||||
}
|
||||
|
||||
type PageSearchResult {
|
||||
id: String!
|
||||
title: String!
|
||||
description: String!
|
||||
path: String!
|
||||
locale: String!
|
||||
id: String
|
||||
title: String
|
||||
description: String
|
||||
path: String
|
||||
locale: String
|
||||
}
|
||||
|
||||
type PageListItem {
|
||||
id: Int!
|
||||
path: String!
|
||||
locale: String!
|
||||
id: Int
|
||||
path: String
|
||||
locale: String
|
||||
title: String
|
||||
description: String
|
||||
contentType: String!
|
||||
isPublished: Boolean!
|
||||
isPrivate: Boolean!
|
||||
contentType: String
|
||||
isPublished: Boolean
|
||||
isPrivate: Boolean
|
||||
privateNS: String
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
tags: [String]
|
||||
}
|
||||
|
||||
type PageTreeItem {
|
||||
id: Int!
|
||||
path: String!
|
||||
depth: Int!
|
||||
title: String!
|
||||
isPrivate: Boolean!
|
||||
isFolder: Boolean!
|
||||
id: Int
|
||||
path: String
|
||||
depth: Int
|
||||
title: String
|
||||
isPrivate: Boolean
|
||||
isFolder: Boolean
|
||||
privateNS: String
|
||||
parent: Int
|
||||
pageId: Int
|
||||
locale: String!
|
||||
locale: String
|
||||
}
|
||||
|
||||
type PageLinkItem {
|
||||
id: Int!
|
||||
path: String!
|
||||
title: String!
|
||||
links: [String]!
|
||||
id: Int
|
||||
path: String
|
||||
title: String
|
||||
links: [String]
|
||||
}
|
||||
|
||||
type PageConflictLatest {
|
||||
id: Int!
|
||||
authorId: String!
|
||||
authorName: String!
|
||||
content: String!
|
||||
createdAt: Date!
|
||||
description: String!
|
||||
isPublished: Boolean!
|
||||
locale: String!
|
||||
path: String!
|
||||
id: Int
|
||||
authorId: String
|
||||
authorName: String
|
||||
content: String
|
||||
createdAt: Date
|
||||
description: String
|
||||
isPublished: Boolean
|
||||
locale: String
|
||||
path: String
|
||||
tags: [String]
|
||||
title: String!
|
||||
updatedAt: Date!
|
||||
title: String
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
enum PageOrderBy {
|
||||
|
||||
@@ -3,32 +3,16 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
rendering: RenderingQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
rendering: RenderingMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type RenderingQuery {
|
||||
renderers(
|
||||
filter: String
|
||||
orderBy: String
|
||||
): [Renderer] @auth(requires: ["manage:system"])
|
||||
): [Renderer]
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type RenderingMutation {
|
||||
extend type Mutation {
|
||||
updateRenderers(
|
||||
renderers: [RendererInput]
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
renderers: [RendererInput]!
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -36,9 +20,9 @@ type RenderingMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type Renderer {
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
title: String!
|
||||
isEnabled: Boolean
|
||||
key: String
|
||||
title: String
|
||||
description: String
|
||||
icon: String
|
||||
dependsOn: String
|
||||
|
||||
@@ -2,54 +2,10 @@
|
||||
# SEARCH
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
search: SearchQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
search: SearchMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type SearchQuery {
|
||||
searchEngines(
|
||||
filter: String
|
||||
orderBy: String
|
||||
): [SearchEngine] @auth(requires: ["manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type SearchMutation {
|
||||
updateSearchEngines(
|
||||
engines: [SearchEngineInput]
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
|
||||
rebuildIndex: DefaultResponse @auth(requires: ["manage:system"])
|
||||
rebuildSearchIndex: DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# TYPES
|
||||
# -----------------------------------------------
|
||||
|
||||
type SearchEngine {
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
title: String!
|
||||
description: String
|
||||
logo: String
|
||||
website: String
|
||||
isAvailable: Boolean
|
||||
config: [KeyValuePair]
|
||||
}
|
||||
|
||||
input SearchEngineInput {
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
config: [KeyValuePairInput]
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@ extend type Query {
|
||||
hostname: String!
|
||||
exact: Boolean!
|
||||
): Site @auth(requires: ["manage:system"])
|
||||
|
||||
# Legacy
|
||||
site: SiteQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
@@ -42,59 +39,6 @@ extend type Mutation {
|
||||
deleteSite (
|
||||
id: UUID!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
|
||||
# Legacy
|
||||
site: SiteMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type SiteQuery {
|
||||
config: SiteConfig @auth(requires: ["manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type SiteMutation {
|
||||
updateConfig(
|
||||
host: String
|
||||
title: String
|
||||
description: String
|
||||
robots: [String]
|
||||
analyticsService: String
|
||||
analyticsId: String
|
||||
company: String
|
||||
contentLicense: String
|
||||
logoUrl: String
|
||||
authAutoLogin: Boolean
|
||||
authEnforce2FA: Boolean
|
||||
authHideLocal: Boolean
|
||||
authLoginBgUrl: String
|
||||
authJwtAudience: String
|
||||
authJwtExpiration: String
|
||||
authJwtRenewablePeriod: String
|
||||
featurePageRatings: Boolean
|
||||
featurePageComments: Boolean
|
||||
featurePersonalWikis: Boolean
|
||||
securityOpenRedirect: Boolean
|
||||
securityIframe: Boolean
|
||||
securityReferrerPolicy: Boolean
|
||||
securityTrustProxy: Boolean
|
||||
securitySRI: Boolean
|
||||
securityHSTS: Boolean
|
||||
securityHSTSDuration: Int
|
||||
securityCSP: Boolean
|
||||
securityCSPDirectives: String
|
||||
uploadMaxFileSize: Int
|
||||
uploadMaxFiles: Int
|
||||
uploadScanSVG: Boolean
|
||||
uploadForceDownload: Boolean
|
||||
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -174,7 +118,7 @@ enum SitePageRatingModes {
|
||||
}
|
||||
|
||||
type SiteCreateResponse {
|
||||
status: ResponseStatus
|
||||
operation: Operation
|
||||
site: Site
|
||||
}
|
||||
|
||||
@@ -227,40 +171,3 @@ input SiteThemeInput {
|
||||
showSharingMenu: Boolean
|
||||
showPrintBtn: Boolean
|
||||
}
|
||||
|
||||
# LEGACY
|
||||
|
||||
type SiteConfig {
|
||||
host: String
|
||||
title: String
|
||||
description: String
|
||||
robots: [String]
|
||||
analyticsService: String
|
||||
analyticsId: String
|
||||
company: String
|
||||
contentLicense: String
|
||||
logoUrl: String
|
||||
authAutoLogin: Boolean
|
||||
authEnforce2FA: Boolean
|
||||
authHideLocal: Boolean
|
||||
authLoginBgUrl: String
|
||||
authJwtAudience: String
|
||||
authJwtExpiration: String
|
||||
authJwtRenewablePeriod: String
|
||||
featurePageRatings: Boolean
|
||||
featurePageComments: Boolean
|
||||
featurePersonalWikis: Boolean
|
||||
securityOpenRedirect: Boolean
|
||||
securityIframe: Boolean
|
||||
securityReferrerPolicy: Boolean
|
||||
securityTrustProxy: Boolean
|
||||
securitySRI: Boolean
|
||||
securityHSTS: Boolean
|
||||
securityHSTSDuration: Int
|
||||
securityCSP: Boolean
|
||||
securityCSPDirectives: String
|
||||
uploadMaxFileSize: Int
|
||||
uploadMaxFiles: Int
|
||||
uploadScanSVG: Boolean
|
||||
uploadForceDownload: Boolean
|
||||
}
|
||||
|
||||
@@ -3,35 +3,30 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
storage: StorageQuery
|
||||
storageTargets(
|
||||
siteId: UUID!
|
||||
): [StorageTarget]
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
storage: StorageMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type StorageQuery {
|
||||
targets: [StorageTarget] @auth(requires: ["manage:system"])
|
||||
status: [StorageStatus] @auth(requires: ["manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type StorageMutation {
|
||||
updateTargets(
|
||||
updateStorageTargets(
|
||||
siteId: UUID!
|
||||
targets: [StorageTargetInput]!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
executeAction(
|
||||
targetKey: String!
|
||||
setupStorageTarget(
|
||||
targetId: UUID!
|
||||
state: JSON!
|
||||
): StorageTargetSetupResponse
|
||||
|
||||
destroyStorageTargetSetup(
|
||||
targetId: UUID!
|
||||
): DefaultResponse
|
||||
|
||||
executeStorageAction(
|
||||
targetId: UUID!
|
||||
handler: String!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -39,40 +34,46 @@ type StorageMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type StorageTarget {
|
||||
isAvailable: Boolean!
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
title: String!
|
||||
id: UUID
|
||||
isEnabled: Boolean
|
||||
module: String
|
||||
title: String
|
||||
description: String
|
||||
logo: String
|
||||
icon: String
|
||||
banner: String
|
||||
vendor: String
|
||||
website: String
|
||||
supportedModes: [String]
|
||||
mode: String
|
||||
hasSchedule: Boolean!
|
||||
syncInterval: String
|
||||
syncIntervalDefault: String
|
||||
config: [KeyValuePair]
|
||||
actions: [StorageTargetAction]
|
||||
contentTypes: JSON
|
||||
assetDelivery: JSON
|
||||
versioning: JSON
|
||||
sync: JSON
|
||||
status: JSON
|
||||
setup: JSON
|
||||
config: JSON
|
||||
actions: JSON
|
||||
}
|
||||
|
||||
type StorageTargetSetupResponse {
|
||||
operation: Operation
|
||||
state: JSON
|
||||
}
|
||||
|
||||
input StorageTargetInput {
|
||||
isEnabled: Boolean!
|
||||
key: String!
|
||||
mode: String!
|
||||
id: UUID!
|
||||
module: String!
|
||||
isEnabled: Boolean
|
||||
contentTypes: [String!]
|
||||
largeThreshold: String
|
||||
assetDeliveryFileStreaming: Boolean
|
||||
assetDeliveryDirectAccess: Boolean
|
||||
syncMode: StorageTargetSyncMode
|
||||
syncInterval: String
|
||||
config: [KeyValuePairInput]
|
||||
useVersioning: Boolean
|
||||
config: JSON
|
||||
}
|
||||
|
||||
type StorageStatus {
|
||||
key: String!
|
||||
title: String!
|
||||
status: String!
|
||||
message: String!
|
||||
lastAttempt: String!
|
||||
}
|
||||
|
||||
type StorageTargetAction {
|
||||
handler: String!
|
||||
label: String!
|
||||
hint: String!
|
||||
enum StorageTargetSyncMode {
|
||||
PULL
|
||||
PUSH
|
||||
SYNC
|
||||
}
|
||||
|
||||
@@ -3,50 +3,41 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
system: SystemQuery
|
||||
systemExtensions: [SystemExtension]
|
||||
systemFlags: [SystemFlag]
|
||||
systemInfo: SystemInfo
|
||||
systemSecurity: SystemSecurity
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
system: SystemMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type SystemQuery {
|
||||
flags: [SystemFlag] @auth(requires: ["manage:system"])
|
||||
info: SystemInfo
|
||||
extensions: [SystemExtension]! @auth(requires: ["manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type SystemMutation {
|
||||
updateFlags(
|
||||
updateSystemFlags(
|
||||
flags: [SystemFlagInput]!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
): DefaultResponse
|
||||
|
||||
resetTelemetryClientId: DefaultResponse @auth(requires: ["manage:system"])
|
||||
updateSystemSecurity(
|
||||
authJwtAudience: String
|
||||
authJwtExpiration: String
|
||||
authJwtRenewablePeriod: String
|
||||
corsConfig: String
|
||||
corsMode: SystemSecurityCorsMode
|
||||
cspDirectives: String
|
||||
disallowFloc: Boolean
|
||||
disallowIframe: Boolean
|
||||
disallowOpenRedirect: Boolean
|
||||
enforceCsp: Boolean
|
||||
enforceHsts: Boolean
|
||||
enforceSameOriginReferrerPolicy: Boolean
|
||||
forceAssetDownload: Boolean
|
||||
hstsDuration: Int
|
||||
trustProxy: Boolean
|
||||
uploadMaxFiles: Int
|
||||
uploadMaxFileSize: Int
|
||||
uploadScanSVG: Boolean
|
||||
): DefaultResponse
|
||||
|
||||
setTelemetry(
|
||||
enabled: Boolean!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
|
||||
performUpgrade: DefaultResponse @auth(requires: ["manage:system"])
|
||||
|
||||
importUsersFromV1(
|
||||
mongoDbConnString: String!
|
||||
groupMode: SystemImportUsersGroupMode!
|
||||
): SystemImportUsersResponse @auth(requires: ["manage:system"])
|
||||
|
||||
setHTTPSRedirection(
|
||||
enabled: Boolean!
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
|
||||
renewHTTPSCertificate: DefaultResponse @auth(requires: ["manage:system"])
|
||||
installExtension(
|
||||
key: String!
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -54,8 +45,8 @@ type SystemMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type SystemFlag {
|
||||
key: String!
|
||||
value: Boolean!
|
||||
key: String
|
||||
value: Boolean
|
||||
}
|
||||
|
||||
input SystemFlagInput {
|
||||
@@ -64,35 +55,35 @@ input SystemFlagInput {
|
||||
}
|
||||
|
||||
type SystemInfo {
|
||||
configFile: String @auth(requires: ["manage:system"])
|
||||
cpuCores: Int @auth(requires: ["manage:system"])
|
||||
currentVersion: String @auth(requires: ["manage:system"])
|
||||
dbHost: String @auth(requires: ["manage:system"])
|
||||
dbType: String @auth(requires: ["manage:system"])
|
||||
dbVersion: String @auth(requires: ["manage:system"])
|
||||
groupsTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:groups", "write:groups", "manage:users", "write:users"])
|
||||
hostname: String @auth(requires: ["manage:system"])
|
||||
httpPort: Int @auth(requires: ["manage:system"])
|
||||
httpRedirection: Boolean @auth(requires: ["manage:system"])
|
||||
httpsPort: Int @auth(requires: ["manage:system"])
|
||||
latestVersion: String @auth(requires: ["manage:system"])
|
||||
latestVersionReleaseDate: Date @auth(requires: ["manage:system"])
|
||||
nodeVersion: String @auth(requires: ["manage:system"])
|
||||
operatingSystem: String @auth(requires: ["manage:system"])
|
||||
pagesTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:pages", "delete:pages"])
|
||||
platform: String @auth(requires: ["manage:system"])
|
||||
ramTotal: String @auth(requires: ["manage:system"])
|
||||
sslDomain: String @auth(requires: ["manage:system"])
|
||||
sslExpirationDate: Date @auth(requires: ["manage:system"])
|
||||
sslProvider: String @auth(requires: ["manage:system"])
|
||||
sslStatus: String @auth(requires: ["manage:system"])
|
||||
sslSubscriberEmail: String @auth(requires: ["manage:system"])
|
||||
tagsTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:pages", "delete:pages"])
|
||||
telemetry: Boolean @auth(requires: ["manage:system"])
|
||||
telemetryClientId: String @auth(requires: ["manage:system"])
|
||||
upgradeCapable: Boolean @auth(requires: ["manage:system"])
|
||||
usersTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:groups", "write:groups", "manage:users", "write:users"])
|
||||
workingDirectory: String @auth(requires: ["manage:system"])
|
||||
configFile: String
|
||||
cpuCores: Int
|
||||
currentVersion: String
|
||||
dbHost: String
|
||||
dbType: String
|
||||
dbVersion: String
|
||||
groupsTotal: Int
|
||||
hostname: String
|
||||
httpPort: Int
|
||||
httpRedirection: Boolean
|
||||
httpsPort: Int
|
||||
latestVersion: String
|
||||
latestVersionReleaseDate: Date
|
||||
nodeVersion: String
|
||||
operatingSystem: String
|
||||
pagesTotal: Int
|
||||
platform: String
|
||||
ramTotal: String
|
||||
sslDomain: String
|
||||
sslExpirationDate: Date
|
||||
sslProvider: String
|
||||
sslStatus: String
|
||||
sslSubscriberEmail: String
|
||||
tagsTotal: Int
|
||||
telemetry: Boolean
|
||||
telemetryClientId: String
|
||||
upgradeCapable: Boolean
|
||||
usersTotal: Int
|
||||
workingDirectory: String
|
||||
}
|
||||
|
||||
enum SystemImportUsersGroupMode {
|
||||
@@ -102,7 +93,7 @@ enum SystemImportUsersGroupMode {
|
||||
}
|
||||
|
||||
type SystemImportUsersResponse {
|
||||
responseResult: ResponseStatus
|
||||
operation: Operation
|
||||
usersCount: Int
|
||||
groupsCount: Int
|
||||
failed: [SystemImportUsersResponseFailed]
|
||||
@@ -115,9 +106,38 @@ type SystemImportUsersResponseFailed {
|
||||
}
|
||||
|
||||
type SystemExtension {
|
||||
key: String!
|
||||
title: String!
|
||||
description: String!
|
||||
isInstalled: Boolean!
|
||||
isCompatible: Boolean!
|
||||
key: String
|
||||
title: String
|
||||
description: String
|
||||
isInstalled: Boolean
|
||||
isInstallable: Boolean
|
||||
isCompatible: Boolean
|
||||
}
|
||||
|
||||
type SystemSecurity {
|
||||
authJwtAudience: String
|
||||
authJwtExpiration: String
|
||||
authJwtRenewablePeriod: String
|
||||
corsConfig: String
|
||||
corsMode: SystemSecurityCorsMode
|
||||
cspDirectives: String
|
||||
disallowFloc: Boolean
|
||||
disallowIframe: Boolean
|
||||
disallowOpenRedirect: Boolean
|
||||
enforceCsp: Boolean
|
||||
enforceHsts: Boolean
|
||||
enforceSameOriginReferrerPolicy: Boolean
|
||||
forceAssetDownload: Boolean
|
||||
hstsDuration: Int
|
||||
trustProxy: Boolean
|
||||
uploadMaxFiles: Int
|
||||
uploadMaxFileSize: Int
|
||||
uploadScanSVG: Boolean
|
||||
}
|
||||
|
||||
enum SystemSecurityCorsMode {
|
||||
OFF
|
||||
REFLECT
|
||||
HOSTNAMES
|
||||
REGEX
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# ===============================================
|
||||
# THEMES
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
theming: ThemingQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
theming: ThemingMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type ThemingQuery {
|
||||
themes: [ThemingTheme] @auth(requires: ["manage:theme", "manage:system"])
|
||||
config: ThemingConfig @auth(requires: ["manage:theme", "manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type ThemingMutation {
|
||||
setConfig(
|
||||
theme: String!
|
||||
iconset: String!
|
||||
darkMode: Boolean!
|
||||
injectCSS: String
|
||||
injectHead: String
|
||||
injectBody: String
|
||||
): DefaultResponse @auth(requires: ["manage:theme", "manage:system"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# TYPES
|
||||
# -----------------------------------------------
|
||||
|
||||
type ThemingConfig {
|
||||
theme: String!
|
||||
iconset: String!
|
||||
darkMode: Boolean!
|
||||
injectCSS: String
|
||||
injectHead: String
|
||||
injectBody: String
|
||||
}
|
||||
|
||||
type ThemingTheme {
|
||||
key: String
|
||||
title: String
|
||||
author: String
|
||||
}
|
||||
@@ -3,90 +3,65 @@
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
users: UserQuery
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
users: UserMutation
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# QUERIES
|
||||
# -----------------------------------------------
|
||||
|
||||
type UserQuery {
|
||||
list(
|
||||
users (
|
||||
page: Int
|
||||
pageSize: Int
|
||||
orderBy: UserOrderBy
|
||||
orderByDirection: OrderByDirection
|
||||
# Filter by name / email
|
||||
filter: String
|
||||
orderBy: String
|
||||
): [UserMinimal] @auth(requires: ["write:users", "manage:users", "manage:system"])
|
||||
): [UserMinimal]
|
||||
|
||||
search(
|
||||
query: String!
|
||||
): [UserMinimal] @auth(requires: ["write:groups", "manage:groups", "write:users", "manage:users", "manage:system"])
|
||||
|
||||
single(
|
||||
id: Int!
|
||||
): User @auth(requires: ["manage:users", "manage:system"])
|
||||
userById(
|
||||
id: UUID!
|
||||
): User
|
||||
|
||||
profile: UserProfile
|
||||
|
||||
lastLogins: [UserLastLogin] @auth(requires: ["write:groups", "manage:groups", "write:users", "manage:users", "manage:system"])
|
||||
lastLogins: [UserLastLogin]
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# MUTATIONS
|
||||
# -----------------------------------------------
|
||||
|
||||
type UserMutation {
|
||||
create(
|
||||
extend type Mutation {
|
||||
createUser(
|
||||
email: String!
|
||||
name: String!
|
||||
passwordRaw: String
|
||||
providerKey: String!
|
||||
groups: [Int]!
|
||||
mustChangePassword: Boolean
|
||||
sendWelcomeEmail: Boolean
|
||||
): UserResponse @auth(requires: ["write:users", "manage:users", "manage:system"])
|
||||
password: String!
|
||||
groups: [UUID]!
|
||||
mustChangePassword: Boolean!
|
||||
sendWelcomeEmail: Boolean!
|
||||
): UserResponse
|
||||
|
||||
update(
|
||||
id: Int!
|
||||
email: String
|
||||
name: String
|
||||
newPassword: String
|
||||
groups: [Int]
|
||||
location: String
|
||||
jobTitle: String
|
||||
timezone: String
|
||||
dateFormat: String
|
||||
appearance: String
|
||||
): DefaultResponse @auth(requires: ["manage:users", "manage:system"])
|
||||
updateUser(
|
||||
id: UUID!
|
||||
patch: UserUpdateInput!
|
||||
): DefaultResponse
|
||||
|
||||
delete(
|
||||
id: Int!
|
||||
replaceId: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:users", "manage:system"])
|
||||
deleteUser(
|
||||
id: UUID!
|
||||
replaceId: UUID!
|
||||
): DefaultResponse
|
||||
|
||||
verify(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:users", "manage:system"])
|
||||
verifyUser(
|
||||
id: UUID!
|
||||
): DefaultResponse
|
||||
|
||||
activate(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:users", "manage:system"])
|
||||
activateUser(
|
||||
id: UUID!
|
||||
): DefaultResponse
|
||||
|
||||
deactivate(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:users", "manage:system"])
|
||||
deactivateUser(
|
||||
id: UUID!
|
||||
): DefaultResponse
|
||||
|
||||
enableTFA(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:users", "manage:system"])
|
||||
enableUserTFA(
|
||||
id: UUID!
|
||||
): DefaultResponse
|
||||
|
||||
disableTFA(
|
||||
id: Int!
|
||||
): DefaultResponse @auth(requires: ["manage:users", "manage:system"])
|
||||
disableUserTFA(
|
||||
id: UUID!
|
||||
): DefaultResponse
|
||||
|
||||
resetPassword(
|
||||
resetUserPassword(
|
||||
id: Int!
|
||||
): DefaultResponse
|
||||
|
||||
@@ -110,71 +85,83 @@ type UserMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type UserResponse {
|
||||
responseResult: ResponseStatus!
|
||||
operation: Operation
|
||||
user: User
|
||||
}
|
||||
|
||||
type UserLastLogin {
|
||||
id: Int!
|
||||
name: String!
|
||||
lastLoginAt: Date!
|
||||
id: UUID
|
||||
name: String
|
||||
lastLoginAt: Date
|
||||
}
|
||||
|
||||
type UserMinimal {
|
||||
id: Int!
|
||||
name: String!
|
||||
email: String!
|
||||
providerKey: String!
|
||||
isSystem: Boolean!
|
||||
isActive: Boolean!
|
||||
createdAt: Date!
|
||||
id: UUID
|
||||
name: String
|
||||
email: String
|
||||
isSystem: Boolean
|
||||
isActive: Boolean
|
||||
createdAt: Date
|
||||
lastLoginAt: Date
|
||||
}
|
||||
|
||||
type User {
|
||||
id: Int!
|
||||
name: String!
|
||||
email: String!
|
||||
providerKey: String!
|
||||
providerName: String
|
||||
providerId: String
|
||||
providerIs2FACapable: Boolean
|
||||
isSystem: Boolean!
|
||||
isActive: Boolean!
|
||||
isVerified: Boolean!
|
||||
location: String!
|
||||
jobTitle: String!
|
||||
timezone: String!
|
||||
dateFormat: String!
|
||||
appearance: String!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
id: UUID
|
||||
name: String
|
||||
email: String
|
||||
auth: JSON
|
||||
isSystem: Boolean
|
||||
isActive: Boolean
|
||||
isVerified: Boolean
|
||||
meta: JSON
|
||||
prefs: JSON
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
lastLoginAt: Date
|
||||
tfaIsActive: Boolean!
|
||||
groups: [Group]!
|
||||
groups: [Group]
|
||||
}
|
||||
|
||||
type UserProfile {
|
||||
id: Int!
|
||||
name: String!
|
||||
email: String!
|
||||
id: Int
|
||||
name: String
|
||||
email: String
|
||||
providerKey: String
|
||||
providerName: String
|
||||
isSystem: Boolean!
|
||||
isVerified: Boolean!
|
||||
location: String!
|
||||
jobTitle: String!
|
||||
timezone: String!
|
||||
dateFormat: String!
|
||||
appearance: String!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
isSystem: Boolean
|
||||
isVerified: Boolean
|
||||
location: String
|
||||
jobTitle: String
|
||||
timezone: String
|
||||
dateFormat: String
|
||||
appearance: String
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
lastLoginAt: Date
|
||||
groups: [String]!
|
||||
pagesTotal: Int!
|
||||
groups: [String]
|
||||
pagesTotal: Int
|
||||
}
|
||||
|
||||
type UserTokenResponse {
|
||||
responseResult: ResponseStatus!
|
||||
operation: Operation
|
||||
jwt: String
|
||||
}
|
||||
|
||||
enum UserOrderBy {
|
||||
id
|
||||
email
|
||||
name
|
||||
createdAt
|
||||
updatedAt
|
||||
lastLoginAt
|
||||
}
|
||||
|
||||
input UserUpdateInput {
|
||||
email: String
|
||||
name: String
|
||||
newPassword: String
|
||||
groups: [UUID!]
|
||||
isActive: Boolean
|
||||
isVerified: Boolean
|
||||
meta: JSON
|
||||
prefs: JSON
|
||||
}
|
||||
|
||||
@@ -1,114 +1,135 @@
|
||||
<template lang="pug">
|
||||
q-dialog(ref='dialog', @hide='onDialogHide')
|
||||
q-dialog(ref='dialogRef', @hide='onDialogHide')
|
||||
q-card(style='min-width: 350px; max-width: 450px;')
|
||||
q-card-section.card-header
|
||||
q-icon(name='img:/_assets/icons/fluent-shutdown.svg', left, size='sm')
|
||||
span {{value ? $t(`admin.sites.activate`) : $t(`admin.sites.deactivate`)}}
|
||||
span {{modelValue ? t(`admin.sites.activate`) : t(`admin.sites.deactivate`)}}
|
||||
q-card-section
|
||||
.text-body2
|
||||
i18n-t(:keypath='value ? `admin.sites.activateConfirm` : `admin.sites.deactivateConfirm`')
|
||||
i18n-t(:keypath='modelValue ? `admin.sites.activateConfirm` : `admin.sites.deactivateConfirm`')
|
||||
template(v-slot:siteTitle)
|
||||
strong {{site.title}}
|
||||
strong {{props.site.title}}
|
||||
q-card-actions.card-actions
|
||||
q-space
|
||||
q-btn.acrylic-btn(
|
||||
flat
|
||||
:label='$t(`common.actions.cancel`)'
|
||||
:label='t(`common.actions.cancel`)'
|
||||
color='grey'
|
||||
padding='xs md'
|
||||
@click='hide'
|
||||
@click='onDialogCancel'
|
||||
)
|
||||
q-btn(
|
||||
unelevated
|
||||
:label='value ? $t(`common.actions.activate`) : $t(`common.actions.deactivate`)'
|
||||
:color='value ? `positive` : `negative`'
|
||||
:label='modelValue ? t(`common.actions.activate`) : t(`common.actions.deactivate`)'
|
||||
:color='modelValue ? `positive` : `negative`'
|
||||
padding='xs md'
|
||||
@click='confirm'
|
||||
:loading='state.isLoading'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import gql from 'graphql-tag'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDialogPluginComponent, useQuasar } from 'quasar'
|
||||
import { reactive, ref } from 'vue'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
site: {
|
||||
type: Object
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
import { useAdminStore } from '../stores/admin'
|
||||
|
||||
// PROPS
|
||||
|
||||
const props = defineProps({
|
||||
site: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
emits: ['ok', 'hide'],
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show () {
|
||||
this.$refs.dialog.show()
|
||||
},
|
||||
hide () {
|
||||
this.$refs.dialog.hide()
|
||||
},
|
||||
onDialogHide () {
|
||||
this.$emit('hide')
|
||||
},
|
||||
async confirm () {
|
||||
try {
|
||||
const siteId = this.site.id
|
||||
const resp = await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation updateSite (
|
||||
$id: UUID!
|
||||
$newState: Boolean
|
||||
) {
|
||||
updateSite(
|
||||
id: $id
|
||||
patch: {
|
||||
isEnabled: $newState
|
||||
}
|
||||
) {
|
||||
status {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// EMITS
|
||||
|
||||
defineEmits([
|
||||
...useDialogPluginComponent.emits
|
||||
])
|
||||
|
||||
// QUASAR
|
||||
|
||||
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
|
||||
const $q = useQuasar()
|
||||
|
||||
// STORES
|
||||
|
||||
const adminStore = useAdminStore()
|
||||
|
||||
// I18N
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
// DATA
|
||||
|
||||
const state = reactive({
|
||||
isLoading: false
|
||||
})
|
||||
|
||||
// METHODS
|
||||
|
||||
async function confirm () {
|
||||
state.isLoading = true
|
||||
try {
|
||||
const resp = await APOLLO_CLIENT.mutate({
|
||||
mutation: gql`
|
||||
mutation updateSite (
|
||||
$id: UUID!
|
||||
$newState: Boolean
|
||||
) {
|
||||
updateSite(
|
||||
id: $id
|
||||
patch: {
|
||||
isEnabled: $newState
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: siteId,
|
||||
newState: this.value
|
||||
) {
|
||||
operation {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: props.site.id,
|
||||
newState: props.modelValue
|
||||
}
|
||||
})
|
||||
if (resp?.data?.updateSite?.operation?.succeeded) {
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: this.$t('admin.sites.updateSuccess')
|
||||
})
|
||||
adminStore.$patch({
|
||||
sites: adminStore.sites.map(s => {
|
||||
if (s.id === props.site.id) {
|
||||
const ns = cloneDeep(s)
|
||||
ns.isEnabled = props.modelValue
|
||||
return ns
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
})
|
||||
if (resp?.data?.updateSite?.status?.succeeded) {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: this.$t('admin.sites.updateSuccess')
|
||||
})
|
||||
this.$store.set('admin/sites', this.$store.get('admin/sites').map(s => {
|
||||
if (s.id === siteId) {
|
||||
const ns = cloneDeep(s)
|
||||
ns.isEnabled = this.value
|
||||
return ns
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
}))
|
||||
this.$emit('ok')
|
||||
this.hide()
|
||||
} else {
|
||||
throw new Error(resp?.data?.updateSite?.status?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
})
|
||||
onDialogOK()
|
||||
} else {
|
||||
throw new Error(resp?.data?.updateSite?.operation?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
state.isLoading = false
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -66,6 +66,8 @@ import { reactive, ref } from 'vue'
|
||||
|
||||
import { useAdminStore } from '../stores/admin'
|
||||
|
||||
// EMITS
|
||||
|
||||
defineEmits([
|
||||
...useDialogPluginComponent.emits
|
||||
])
|
||||
@@ -114,7 +116,7 @@ async function create () {
|
||||
hostname: $hostname
|
||||
title: $title
|
||||
) {
|
||||
status {
|
||||
operation {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
@@ -126,7 +128,7 @@ async function create () {
|
||||
title: state.siteName
|
||||
}
|
||||
})
|
||||
if (resp?.data?.createSite?.status?.succeeded) {
|
||||
if (resp?.data?.createSite?.operation?.succeeded) {
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: t('admin.sites.createSuccess')
|
||||
@@ -134,7 +136,7 @@ async function create () {
|
||||
await adminStore.fetchSites()
|
||||
onDialogOK()
|
||||
} else {
|
||||
throw new Error(resp?.data?.createSite?.status?.message || 'An unexpected error occured.')
|
||||
throw new Error(resp?.data?.createSite?.operation?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
|
||||
@@ -1,94 +1,115 @@
|
||||
<template lang="pug">
|
||||
q-dialog(ref='dialog', @hide='onDialogHide')
|
||||
q-dialog(ref='dialogRef', @hide='onDialogHide')
|
||||
q-card(style='min-width: 350px; max-width: 450px;')
|
||||
q-card-section.card-header
|
||||
q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
|
||||
span {{$t(`admin.sites.delete`)}}
|
||||
span {{t(`admin.sites.delete`)}}
|
||||
q-card-section
|
||||
.text-body2
|
||||
i18n-t(keypath='admin.sites.deleteConfirm')
|
||||
template(v-slot:siteTitle)
|
||||
strong {{site.title}}
|
||||
strong {{props.site.title}}
|
||||
.text-body2.q-mt-md
|
||||
strong.text-negative {{$t(`admin.sites.deleteConfirmWarn`)}}
|
||||
strong.text-negative {{t(`admin.sites.deleteConfirmWarn`)}}
|
||||
q-card-actions.card-actions
|
||||
q-space
|
||||
q-btn.acrylic-btn(
|
||||
flat
|
||||
:label='$t(`common.actions.cancel`)'
|
||||
:label='t(`common.actions.cancel`)'
|
||||
color='grey'
|
||||
padding='xs md'
|
||||
@click='hide'
|
||||
@click='onDialogCancel'
|
||||
)
|
||||
q-btn(
|
||||
unelevated
|
||||
:label='$t(`common.actions.delete`)'
|
||||
:label='t(`common.actions.delete`)'
|
||||
color='negative'
|
||||
padding='xs md'
|
||||
@click='confirm'
|
||||
:loading='state.isLoading'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import gql from 'graphql-tag'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDialogPluginComponent, useQuasar } from 'quasar'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
site: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
emits: ['ok', 'hide'],
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show () {
|
||||
this.$refs.dialog.show()
|
||||
},
|
||||
hide () {
|
||||
this.$refs.dialog.hide()
|
||||
},
|
||||
onDialogHide () {
|
||||
this.$emit('hide')
|
||||
},
|
||||
async confirm () {
|
||||
try {
|
||||
const siteId = this.site.id
|
||||
const resp = await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation deleteSite ($id: UUID!) {
|
||||
deleteSite(id: $id) {
|
||||
status {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: siteId
|
||||
}
|
||||
})
|
||||
if (resp?.data?.deleteSite?.status?.succeeded) {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: this.$t('admin.sites.deleteSuccess')
|
||||
})
|
||||
this.$store.set('admin/sites', this.$store.get('admin/sites').filter(s => s.id !== siteId))
|
||||
this.$emit('ok')
|
||||
this.hide()
|
||||
} else {
|
||||
throw new Error(resp?.data?.deleteSite?.status?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
}
|
||||
import { useAdminStore } from '../stores/admin'
|
||||
|
||||
// PROPS
|
||||
|
||||
const props = defineProps({
|
||||
site: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
// EMITS
|
||||
|
||||
defineEmits([
|
||||
...useDialogPluginComponent.emits
|
||||
])
|
||||
|
||||
// QUASAR
|
||||
|
||||
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
|
||||
const $q = useQuasar()
|
||||
|
||||
// STORES
|
||||
|
||||
const adminStore = useAdminStore()
|
||||
|
||||
// I18N
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
// DATA
|
||||
|
||||
const state = reactive({
|
||||
isLoading: false
|
||||
})
|
||||
|
||||
// METHODS
|
||||
|
||||
async function confirm () {
|
||||
state.isLoading = true
|
||||
try {
|
||||
const resp = await APOLLO_CLIENT.mutate({
|
||||
mutation: gql`
|
||||
mutation deleteSite ($id: UUID!) {
|
||||
deleteSite(id: $id) {
|
||||
status {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: props.site.id
|
||||
}
|
||||
})
|
||||
if (resp?.data?.deleteSite?.status?.succeeded) {
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: t('admin.sites.deleteSuccess')
|
||||
})
|
||||
adminStore.$patch({
|
||||
sites: adminStore.sites.filter(s => s.id !== props.site.id)
|
||||
})
|
||||
onDialogOK()
|
||||
} else {
|
||||
throw new Error(resp?.data?.deleteSite?.status?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
state.isLoading = false
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -133,10 +133,6 @@ useMeta({
|
||||
title: t('admin.sites.title')
|
||||
})
|
||||
|
||||
// DATA
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
// METHODS
|
||||
|
||||
async function refresh () {
|
||||
|
||||
Reference in New Issue
Block a user