mirror of https://github.com/Requarks/wiki.git
30 changed files with 1361 additions and 1093 deletions
Split View
Diff Options
-
4client/components/admin/admin-general.vue
-
98client/components/admin/admin-locale.vue
-
10client/components/admin/admin-theme.vue
-
21client/components/admin/admin-utilities.vue
-
45client/components/setup.vue
-
14client/graph/admin-locale-query-list.gql
-
1config.sample.yml
-
53dev/webpack/webpack.dev.js
-
64dev/webpack/webpack.prod.js
-
109package.json
-
87server/app/data.yml
-
10server/core/kernel.js
-
9server/core/localization.js
-
5server/core/logger.js
-
29server/core/queue.js
-
18server/core/worker.js
-
46server/graph/resolvers/localization.js
-
45server/graph/schemas/localization.graphql
-
2server/index.js
-
33server/jobs/purge-uploads.js
-
69server/jobs/sync-git.js
-
38server/jobs/sync-graph-locales.js
-
38server/locales/default.json
-
39server/models/locale.js
-
68server/queues/git-sync.js
-
29server/queues/upl-clear-temp.js
-
12server/setup.js
-
2server/views/main/setup.pug
-
62server/worker.js
-
1394yarn.lock
@ -0,0 +1,14 @@ |
|||
{ |
|||
localization { |
|||
locales { |
|||
code |
|||
createdAt |
|||
isInstalled |
|||
installDate |
|||
isRTL |
|||
name |
|||
nativeName |
|||
updatedAt |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
const path = require('path') |
|||
|
|||
let WIKI = { |
|||
IS_DEBUG: process.env.NODE_ENV === 'development', |
|||
ROOTPATH: process.cwd(), |
|||
SERVERPATH: path.join(process.cwd(), 'server'), |
|||
Error: require('../helpers/error'), |
|||
configSvc: require('./config') |
|||
} |
|||
global.WIKI = WIKI |
|||
|
|||
WIKI.configSvc.init() |
|||
|
|||
// ----------------------------------------
|
|||
// Init Logger
|
|||
// ----------------------------------------
|
|||
|
|||
WIKI.logger = require('./logger').init('JOB') |
@ -0,0 +1,46 @@ |
|||
const graphHelper = require('../../helpers/graph') |
|||
const _ = require('lodash') |
|||
|
|||
/* global WIKI */ |
|||
|
|||
module.exports = { |
|||
Query: { |
|||
async localization() { return {} } |
|||
}, |
|||
Mutation: { |
|||
async localization() { return {} } |
|||
}, |
|||
LocalizationQuery: { |
|||
async locales(obj, args, context, info) { |
|||
let remoteLocales = await WIKI.redis.get('locales') |
|||
let localLocales = await WIKI.db.Locale.findAll({ |
|||
attributes: { |
|||
exclude: ['strings'] |
|||
}, |
|||
raw: true |
|||
}) |
|||
remoteLocales = (remoteLocales) ? JSON.parse(remoteLocales) : localLocales |
|||
return _.map(remoteLocales, rl => { |
|||
let isInstalled = _.some(localLocales, ['code', rl.code]) |
|||
return { |
|||
...rl, |
|||
isInstalled, |
|||
installDate: isInstalled ? _.find(localLocales, ['code', rl.code]).updatedAt : null |
|||
} |
|||
}) |
|||
} |
|||
}, |
|||
LocalizationMutation: { |
|||
async updateLocale(obj, args, context) { |
|||
try { |
|||
let authResult = await WIKI.db.User.login(args, context) |
|||
return { |
|||
...authResult, |
|||
responseResult: graphHelper.generateSuccess('Login success') |
|||
} |
|||
} catch (err) { |
|||
return graphHelper.generateError(err) |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,45 @@ |
|||
# =============================================== |
|||
# LOCALIZATION |
|||
# =============================================== |
|||
|
|||
extend type Query { |
|||
localization: LocalizationQuery |
|||
} |
|||
|
|||
extend type Mutation { |
|||
localization: LocalizationMutation |
|||
} |
|||
|
|||
# ----------------------------------------------- |
|||
# QUERIES |
|||
# ----------------------------------------------- |
|||
|
|||
type LocalizationQuery { |
|||
locales: [LocalizationLocale] |
|||
} |
|||
|
|||
# ----------------------------------------------- |
|||
# MUTATIONS |
|||
# ----------------------------------------------- |
|||
|
|||
type LocalizationMutation { |
|||
updateLocale( |
|||
localeId: String! |
|||
autoUpdate: Boolean! |
|||
): DefaultResponse |
|||
} |
|||
|
|||
# ----------------------------------------------- |
|||
# TYPES |
|||
# ----------------------------------------------- |
|||
|
|||
type LocalizationLocale { |
|||
code: String! |
|||
createdAt: Date! |
|||
installDate: Date |
|||
isInstalled: Boolean! |
|||
isRTL: Boolean! |
|||
name: String! |
|||
nativeName: String! |
|||
updatedAt: Date! |
|||
} |
@ -0,0 +1,33 @@ |
|||
require('../core/worker') |
|||
|
|||
/* global WIKI */ |
|||
|
|||
const Promise = require('bluebird') |
|||
const fs = Promise.promisifyAll(require('fs-extra')) |
|||
const moment = require('moment') |
|||
const path = require('path') |
|||
|
|||
module.exports = async (job) => { |
|||
WIKI.logger.info('Purging orphaned upload files...') |
|||
|
|||
try { |
|||
const uplTempPath = path.resolve(process.cwd(), WIKI.config.paths.data, 'temp-upload') |
|||
const ls = await fs.readdirAsync(uplTempPath) |
|||
const fifteenAgo = moment().subtract(15, 'minutes') |
|||
|
|||
await Promise.map(ls, (f) => { |
|||
return fs.statAsync(path.join(uplTempPath, f)).then((s) => { return { filename: f, stat: s } }) |
|||
}).filter((s) => { return s.stat.isFile() }).then((arrFiles) => { |
|||
return Promise.map(arrFiles, (f) => { |
|||
if (moment(f.stat.ctime).isBefore(fifteenAgo, 'minute')) { |
|||
return fs.unlinkAsync(path.join(uplTempPath, f.filename)) |
|||
} |
|||
}) |
|||
}) |
|||
|
|||
WIKI.logger.info('Purging orphaned upload files: [ COMPLETED ]') |
|||
} catch (err) { |
|||
WIKI.logger.error('Purging orphaned upload files: [ FAILED ]') |
|||
WIKI.logger.error(err.message) |
|||
} |
|||
} |
@ -0,0 +1,69 @@ |
|||
'use strict' |
|||
|
|||
// /* global WIKI */
|
|||
|
|||
// const Promise = require('bluebird')
|
|||
// const fs = Promise.promisifyAll(require('fs-extra'))
|
|||
// const klaw = require('klaw')
|
|||
// const moment = require('moment')
|
|||
// const path = require('path')
|
|||
// const entryHelper = require('../helpers/entry')
|
|||
|
|||
module.exports = (job) => { |
|||
return true |
|||
// return WIKI.git.resync().then(() => {
|
|||
// // -> Stream all documents
|
|||
|
|||
// let cacheJobs = []
|
|||
// let jobCbStreamDocsResolve = null
|
|||
// let jobCbStreamDocs = new Promise((resolve, reject) => {
|
|||
// jobCbStreamDocsResolve = resolve
|
|||
// })
|
|||
|
|||
// klaw(WIKI.REPOPATH).on('data', function (item) {
|
|||
// if (path.extname(item.path) === '.md' && path.basename(item.path) !== 'README.md') {
|
|||
// let entryPath = entryHelper.parsePath(entryHelper.getEntryPathFromFullPath(item.path))
|
|||
// let cachePath = entryHelper.getCachePath(entryPath)
|
|||
|
|||
// // -> Purge outdated cache
|
|||
|
|||
// cacheJobs.push(
|
|||
// fs.statAsync(cachePath).then((st) => {
|
|||
// return moment(st.mtime).isBefore(item.stats.mtime) ? 'expired' : 'active'
|
|||
// }).catch((err) => {
|
|||
// return (err.code !== 'EEXIST') ? err : 'new'
|
|||
// }).then((fileStatus) => {
|
|||
// // -> Delete expired cache file
|
|||
|
|||
// if (fileStatus === 'expired') {
|
|||
// return fs.unlinkAsync(cachePath).return(fileStatus)
|
|||
// }
|
|||
|
|||
// return fileStatus
|
|||
// }).then((fileStatus) => {
|
|||
// // -> Update cache and search index
|
|||
|
|||
// if (fileStatus !== 'active') {
|
|||
// return global.entries.updateCache(entryPath).then(entry => {
|
|||
// process.send({
|
|||
// action: 'searchAdd',
|
|||
// content: entry
|
|||
// })
|
|||
// return true
|
|||
// })
|
|||
// }
|
|||
|
|||
// return true
|
|||
// })
|
|||
// )
|
|||
// }
|
|||
// }).on('end', () => {
|
|||
// jobCbStreamDocsResolve(Promise.all(cacheJobs))
|
|||
// })
|
|||
|
|||
// return jobCbStreamDocs
|
|||
// }).then(() => {
|
|||
// WIKI.logger.info('Git remote repository sync: DONE')
|
|||
// return true
|
|||
// })
|
|||
} |
@ -0,0 +1,38 @@ |
|||
require('../core/worker') |
|||
const _ = require('lodash') |
|||
const { createApolloFetch } = require('apollo-fetch') |
|||
|
|||
/* global WIKI */ |
|||
|
|||
WIKI.redis = require('../core/redis').init() |
|||
const apollo = createApolloFetch({ |
|||
uri: 'https://graph.requarks.io' |
|||
}) |
|||
|
|||
module.exports = async (job) => { |
|||
WIKI.logger.info('Syncing locales with Graph endpoint...') |
|||
|
|||
try { |
|||
const resp = await apollo({ |
|||
query: `{
|
|||
localization { |
|||
locales { |
|||
code |
|||
name |
|||
nativeName |
|||
isRTL |
|||
createdAt |
|||
updatedAt |
|||
} |
|||
} |
|||
}`
|
|||
}) |
|||
const locales = _.sortBy(_.get(resp, 'data.localization.locales', []), 'name').map(lc => ({...lc, isInstalled: (lc.code === 'en')})) |
|||
WIKI.redis.set('locales', JSON.stringify(locales)) |
|||
|
|||
WIKI.logger.info('Syncing locales with Graph endpoint: [ COMPLETED ]') |
|||
} catch (err) { |
|||
WIKI.logger.error('Syncing locales with Graph endpoint: [ FAILED ]') |
|||
WIKI.logger.error(err.message) |
|||
} |
|||
} |
@ -0,0 +1,38 @@ |
|||
{ |
|||
"auth": { |
|||
"actions": { |
|||
"login": "Log In" |
|||
}, |
|||
"errors": { |
|||
"invalidLogin": "Invalid Login", |
|||
"invalidLoginMsg": "The email or password is invalid.", |
|||
"invalidUserEmail": "Invalid User Email", |
|||
"loginError": "Login error", |
|||
"notYetAuthorized": "You have not been authorized to login to this site yet.", |
|||
"tooManyAttempts": "Too many attempts!", |
|||
"tooManyAttemptsMsg": "You've made too many failed attempts in a short period of time, please try again {{time}}.", |
|||
"userNotFound": "User not found" |
|||
}, |
|||
"fields": { |
|||
"emailUser": "Email / Username", |
|||
"password": "Password" |
|||
}, |
|||
"loginRequired": "Login required", |
|||
"providers": { |
|||
"azure": "Azure Active Directory", |
|||
"facebook": "Facebook", |
|||
"github": "GitHub", |
|||
"google": "Google ID", |
|||
"ldap": "LDAP / Active Directory", |
|||
"local": "Local", |
|||
"slack": "Slack", |
|||
"windowslive": "Microsoft Account" |
|||
}, |
|||
"tfa": { |
|||
"placeholder": "XXXXXX", |
|||
"subtitle": "Security code required:", |
|||
"title": "Two Factor Authentication", |
|||
"verifyToken": "Verify" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Locale schema |
|||
*/ |
|||
module.exports = (sequelize, DataTypes) => { |
|||
let localeSchema = sequelize.define('locale', { |
|||
code: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false |
|||
}, |
|||
strings: { |
|||
type: DataTypes.JSON, |
|||
allowNull: true |
|||
}, |
|||
isRTL: { |
|||
type: DataTypes.BOOLEAN, |
|||
allowNull: false, |
|||
defaultValue: false |
|||
}, |
|||
name: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false |
|||
}, |
|||
nativeName: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false |
|||
} |
|||
}, { |
|||
timestamps: true, |
|||
version: true, |
|||
indexes: [ |
|||
{ |
|||
unique: true, |
|||
fields: ['code'] |
|||
} |
|||
] |
|||
}) |
|||
|
|||
return localeSchema |
|||
} |
@ -1,68 +0,0 @@ |
|||
'use strict' |
|||
|
|||
/* global WIKI */ |
|||
|
|||
const Promise = require('bluebird') |
|||
const fs = Promise.promisifyAll(require('fs-extra')) |
|||
const klaw = require('klaw') |
|||
const moment = require('moment') |
|||
const path = require('path') |
|||
const entryHelper = require('../helpers/entry') |
|||
|
|||
module.exports = (job) => { |
|||
return WIKI.git.resync().then(() => { |
|||
// -> Stream all documents
|
|||
|
|||
let cacheJobs = [] |
|||
let jobCbStreamDocsResolve = null |
|||
let jobCbStreamDocs = new Promise((resolve, reject) => { |
|||
jobCbStreamDocsResolve = resolve |
|||
}) |
|||
|
|||
klaw(WIKI.REPOPATH).on('data', function (item) { |
|||
if (path.extname(item.path) === '.md' && path.basename(item.path) !== 'README.md') { |
|||
let entryPath = entryHelper.parsePath(entryHelper.getEntryPathFromFullPath(item.path)) |
|||
let cachePath = entryHelper.getCachePath(entryPath) |
|||
|
|||
// -> Purge outdated cache
|
|||
|
|||
cacheJobs.push( |
|||
fs.statAsync(cachePath).then((st) => { |
|||
return moment(st.mtime).isBefore(item.stats.mtime) ? 'expired' : 'active' |
|||
}).catch((err) => { |
|||
return (err.code !== 'EEXIST') ? err : 'new' |
|||
}).then((fileStatus) => { |
|||
// -> Delete expired cache file
|
|||
|
|||
if (fileStatus === 'expired') { |
|||
return fs.unlinkAsync(cachePath).return(fileStatus) |
|||
} |
|||
|
|||
return fileStatus |
|||
}).then((fileStatus) => { |
|||
// -> Update cache and search index
|
|||
|
|||
if (fileStatus !== 'active') { |
|||
return global.entries.updateCache(entryPath).then(entry => { |
|||
process.send({ |
|||
action: 'searchAdd', |
|||
content: entry |
|||
}) |
|||
return true |
|||
}) |
|||
} |
|||
|
|||
return true |
|||
}) |
|||
) |
|||
} |
|||
}).on('end', () => { |
|||
jobCbStreamDocsResolve(Promise.all(cacheJobs)) |
|||
}) |
|||
|
|||
return jobCbStreamDocs |
|||
}).then(() => { |
|||
WIKI.logger.info('Git remote repository sync: DONE') |
|||
return true |
|||
}) |
|||
} |
@ -1,29 +0,0 @@ |
|||
'use strict' |
|||
|
|||
/* global WIKI */ |
|||
|
|||
const Promise = require('bluebird') |
|||
const fs = Promise.promisifyAll(require('fs-extra')) |
|||
const moment = require('moment') |
|||
const path = require('path') |
|||
|
|||
module.exports = (job) => { |
|||
return fs.readdirAsync(WIKI.UPLTEMPPATH).then((ls) => { |
|||
let fifteenAgo = moment().subtract(15, 'minutes') |
|||
|
|||
return Promise.map(ls, (f) => { |
|||
return fs.statAsync(path.join(WIKI.UPLTEMPPATH, f)).then((s) => { return { filename: f, stat: s } }) |
|||
}).filter((s) => { return s.stat.isFile() }).then((arrFiles) => { |
|||
return Promise.map(arrFiles, (f) => { |
|||
if (moment(f.stat.ctime).isBefore(fifteenAgo, 'minute')) { |
|||
return fs.unlinkAsync(path.join(WIKI.UPLTEMPPATH, f.filename)) |
|||
} else { |
|||
return true |
|||
} |
|||
}) |
|||
}) |
|||
}).then(() => { |
|||
WIKI.logger.info('Purging temporary upload files: DONE') |
|||
return true |
|||
}) |
|||
} |
@ -1,62 +0,0 @@ |
|||
const Promise = require('bluebird') |
|||
|
|||
/* global WIKI */ |
|||
|
|||
module.exports = Promise.join( |
|||
WIKI.db.onReady, |
|||
WIKI.configSvc.loadFromDb(['features', 'logging', 'site', 'uploads']) |
|||
).then(() => { |
|||
const path = require('path') |
|||
|
|||
WIKI.REPOPATH = path.resolve(WIKI.ROOTPATH, WIKI.config.paths.repo) |
|||
WIKI.DATAPATH = path.resolve(WIKI.ROOTPATH, WIKI.config.paths.data) |
|||
WIKI.UPLTEMPPATH = path.join(WIKI.DATAPATH, 'temp-upload') |
|||
|
|||
// ----------------------------------------
|
|||
// Load global modules
|
|||
// ----------------------------------------
|
|||
|
|||
WIKI.lang = require('i18next') |
|||
|
|||
// ----------------------------------------
|
|||
// Localization Engine
|
|||
// ----------------------------------------
|
|||
|
|||
const i18nBackend = require('i18next-node-fs-backend') |
|||
WIKI.lang.use(i18nBackend).init({ |
|||
load: 'languageOnly', |
|||
ns: ['common', 'admin', 'auth', 'errors'], |
|||
defaultNS: 'common', |
|||
saveMissing: false, |
|||
preload: [WIKI.config.lang], |
|||
lng: WIKI.config.lang, |
|||
fallbackLng: 'en', |
|||
backend: { |
|||
loadPath: path.join(WIKI.SERVERPATH, 'locales/{{lng}}/{{ns}}.yml') |
|||
} |
|||
}) |
|||
|
|||
// ----------------------------------------
|
|||
// Start Queues
|
|||
// ----------------------------------------
|
|||
|
|||
const Bull = require('bull') |
|||
const autoload = require('auto-load') |
|||
|
|||
let queues = autoload(path.join(WIKI.SERVERPATH, 'queues')) |
|||
|
|||
for (let queueName in queues) { |
|||
new Bull(queueName, { |
|||
prefix: `q-${WIKI.config.ha.nodeuid}`, |
|||
redis: WIKI.config.redis |
|||
}).process(queues[queueName]) |
|||
} |
|||
|
|||
// ----------------------------------------
|
|||
// Shutdown gracefully
|
|||
// ----------------------------------------
|
|||
|
|||
process.on('disconnect', () => { |
|||
process.exit() |
|||
}) |
|||
}) |
1394
yarn.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save