wiki-server 0.26.0-rc.1 → 0.26.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +7 -3
- package/lib/defaultargs.js +13 -7
- package/lib/forward.js +1 -2
- package/lib/page.js +84 -44
- package/lib/plugins.js +6 -9
- package/lib/random_id.js +1 -3
- package/lib/search.js +13 -9
- package/lib/security.js +12 -3
- package/lib/server.js +180 -105
- package/lib/sitemap.js +7 -8
- package/package.json +17 -15
package/index.js
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
// Simple file so that if you require this directory
|
|
3
3
|
// in node it instead requires ./lib/server.coffee
|
|
4
4
|
// with coffee-script already loaded.
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
// import('coffeescript')
|
|
6
|
+
// import('coffeescript/register.js')
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// const { default: server } = await import('./lib/server.js')
|
|
9
|
+
// export default server
|
|
10
|
+
|
|
11
|
+
import { default as server } from './lib/server.js'
|
|
12
|
+
export default server
|
package/lib/defaultargs.js
CHANGED
|
@@ -9,13 +9,19 @@
|
|
|
9
9
|
// **defaultargs.coffee** when called on the argv object this
|
|
10
10
|
// module will create reasonable defaults for options not supplied,
|
|
11
11
|
// based on what information is provided.
|
|
12
|
-
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import { randomBytes } from 'node:crypto'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import { dirname } from 'node:path'
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
18
|
+
const __dirname = dirname(__filename)
|
|
13
19
|
|
|
14
20
|
const getUserHome = () => {
|
|
15
21
|
return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE
|
|
16
22
|
}
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
export default argv => {
|
|
19
25
|
argv = argv || {}
|
|
20
26
|
argv.root ||= __dirname
|
|
21
27
|
// the directory that contains all the packages that makeup the wiki
|
|
@@ -32,7 +38,7 @@ module.exports = argv => {
|
|
|
32
38
|
argv.url ||= `http://localhost${argv.port === 80 ? '' : ':' + argv.port}`
|
|
33
39
|
argv.id ||= path.join(argv.status, 'owner.json')
|
|
34
40
|
argv.uploadLimit ||= '5mb'
|
|
35
|
-
argv.cookieSecret ||=
|
|
41
|
+
argv.cookieSecret ||= randomBytes(64).toString('hex')
|
|
36
42
|
argv.secure_cookie ||= false
|
|
37
43
|
argv.session_duration ||= 7
|
|
38
44
|
argv.neighbors ||= ''
|
|
@@ -43,9 +49,9 @@ module.exports = argv => {
|
|
|
43
49
|
argv.database = JSON.parse(argv.database)
|
|
44
50
|
}
|
|
45
51
|
argv.database ||= {}
|
|
46
|
-
argv.database.type ||= './page'
|
|
52
|
+
argv.database.type ||= './page.js'
|
|
47
53
|
if (argv.database.type.charAt(0) === '.') {
|
|
48
|
-
if (argv.database.type != './page') {
|
|
54
|
+
if (argv.database.type != './page.js') {
|
|
49
55
|
console.log('\n\nWARNING: This storage option is depeciated.')
|
|
50
56
|
console.log(' See ReadMe for details of the changes required.\n\n')
|
|
51
57
|
}
|
|
@@ -53,8 +59,8 @@ module.exports = argv => {
|
|
|
53
59
|
argv.database.type = 'wiki-storage-' + argv.database.type
|
|
54
60
|
}
|
|
55
61
|
|
|
56
|
-
argv.security_type ||= './security'
|
|
57
|
-
if (argv.security_type === './security') {
|
|
62
|
+
argv.security_type ||= './security.js'
|
|
63
|
+
if (argv.security_type === './security.js') {
|
|
58
64
|
console.log('\n\nINFORMATION: Using default security module.')
|
|
59
65
|
} else {
|
|
60
66
|
argv.security_type = 'wiki-security-' + argv.security_type
|
package/lib/forward.js
CHANGED
package/lib/page.js
CHANGED
|
@@ -10,14 +10,53 @@
|
|
|
10
10
|
// Everything is stored using json flat files.
|
|
11
11
|
|
|
12
12
|
// #### Requires ####
|
|
13
|
-
const fs = require('fs')
|
|
14
|
-
const path = require('path')
|
|
15
|
-
const events = require('events')
|
|
13
|
+
// const fs = require('fs')
|
|
14
|
+
// const path = require('path')
|
|
15
|
+
// const events = require('events')
|
|
16
|
+
// const glob = require('glob')
|
|
17
|
+
//
|
|
18
|
+
// const async = require('async')
|
|
19
|
+
//
|
|
20
|
+
// // const random_id = require('./random_id.cjs')
|
|
21
|
+
// const synopsis = require('wiki-client/lib/synopsis')
|
|
22
|
+
|
|
23
|
+
import fs from 'node:fs'
|
|
24
|
+
import path from 'node:path'
|
|
25
|
+
import url from 'node:url'
|
|
26
|
+
import events from 'node:events'
|
|
27
|
+
|
|
28
|
+
import { createRequire } from 'node:module'
|
|
29
|
+
const require = createRequire(import.meta.url)
|
|
30
|
+
|
|
31
|
+
// Use dynamic import to load package.json from the main application's working directory
|
|
32
|
+
const wikiPackageImport = async () => {
|
|
33
|
+
let done = false
|
|
34
|
+
return new Promise(resolve => {
|
|
35
|
+
import('wiki/package.json', { with: { type: 'json' } })
|
|
36
|
+
.then(imported => {
|
|
37
|
+
done = true
|
|
38
|
+
resolve(imported.default)
|
|
39
|
+
})
|
|
40
|
+
.catch(e => {
|
|
41
|
+
return e
|
|
42
|
+
})
|
|
43
|
+
.then(async () => {
|
|
44
|
+
if (done) return
|
|
45
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json')
|
|
46
|
+
const packageJsonUrl = url.pathToFileURL(packageJsonPath).href
|
|
47
|
+
import(packageJsonUrl, { with: { type: 'json' } })
|
|
48
|
+
.then(imported => {
|
|
49
|
+
resolve(imported.default)
|
|
50
|
+
})
|
|
51
|
+
.catch(e => console.error('problems importing package', e))
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
}
|
|
16
55
|
|
|
17
|
-
const
|
|
56
|
+
const packageJson = await wikiPackageImport()
|
|
18
57
|
|
|
19
|
-
|
|
20
|
-
|
|
58
|
+
// import random_id from './random_id.cjs'; // Uncomment if needed
|
|
59
|
+
import synopsis from 'wiki-client/lib/synopsis.js' // Add correct extension if necessary
|
|
21
60
|
|
|
22
61
|
const asSlug = name =>
|
|
23
62
|
name
|
|
@@ -27,7 +66,7 @@ const asSlug = name =>
|
|
|
27
66
|
|
|
28
67
|
// Export a function that generates a page handler
|
|
29
68
|
// when called with options object.
|
|
30
|
-
|
|
69
|
+
export default argv => {
|
|
31
70
|
const wikiName = new URL(argv.url).hostname
|
|
32
71
|
|
|
33
72
|
fs.mkdir(argv.db, { recursive: true }, e => {
|
|
@@ -36,13 +75,11 @@ module.exports = exports = argv => {
|
|
|
36
75
|
|
|
37
76
|
// create a list of plugin pages.
|
|
38
77
|
const pluginPages = new Map()
|
|
39
|
-
|
|
78
|
+
|
|
79
|
+
Object.keys(packageJson.dependencies)
|
|
40
80
|
.filter(depend => depend.startsWith('wiki-plugin'))
|
|
41
81
|
.forEach(plugin => {
|
|
42
|
-
const pagesPath = path.join(
|
|
43
|
-
path.dirname(require.resolve(`${plugin}/package`, { paths: require.main.paths })),
|
|
44
|
-
'pages',
|
|
45
|
-
)
|
|
82
|
+
const pagesPath = path.join(path.dirname(require.resolve(`${plugin}/package`)), 'pages')
|
|
46
83
|
fs.readdir(pagesPath, { withFileTypes: true }, (err, entries) => {
|
|
47
84
|
if (err) return
|
|
48
85
|
entries.forEach(entry => {
|
|
@@ -379,43 +416,46 @@ module.exports = exports = argv => {
|
|
|
379
416
|
}
|
|
380
417
|
return collaborativeLinks
|
|
381
418
|
}
|
|
419
|
+
|
|
382
420
|
fs.readdir(argv.db, (e, files) => {
|
|
383
421
|
if (e) return cb(e)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
422
|
+
const doSitemap = async file => {
|
|
423
|
+
return new Promise(resolve => {
|
|
424
|
+
itself.get(file, (e, page, status) => {
|
|
425
|
+
if (file.match(/^\./)) return resolve(null)
|
|
426
|
+
if (e || status === 404) {
|
|
427
|
+
console.log('Problem building sitemap:', file, 'e: ', e, 'status:', status)
|
|
428
|
+
return resolve(null) // Ignore errors in the pagehandler get.
|
|
429
|
+
}
|
|
430
|
+
let pageLinksMap
|
|
431
|
+
try {
|
|
432
|
+
pageLinksMap = page.story.reduce(extractPageLinks, new Map())
|
|
433
|
+
} catch (err) {
|
|
434
|
+
console.log(`METADATA *** ${wikiName} reduce to extract links on ${file} failed`, err.message)
|
|
435
|
+
pageLinksMap = []
|
|
436
|
+
}
|
|
437
|
+
//
|
|
438
|
+
const pageLinks = pageLinksMap.size > 0 ? Object.fromEntries(pageLinksMap) : undefined
|
|
439
|
+
|
|
440
|
+
resolve({
|
|
441
|
+
slug: file,
|
|
442
|
+
title: page.title,
|
|
443
|
+
date: editDate(page.journal),
|
|
444
|
+
synopsis: synopsis(page),
|
|
445
|
+
links: pageLinks,
|
|
446
|
+
})
|
|
409
447
|
})
|
|
410
448
|
})
|
|
411
449
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
450
|
+
|
|
451
|
+
Promise.all(files.map(doSitemap))
|
|
452
|
+
.then(sitemap => {
|
|
453
|
+
cb(
|
|
454
|
+
null,
|
|
455
|
+
sitemap.filter(item => item != null),
|
|
456
|
+
)
|
|
457
|
+
})
|
|
458
|
+
.catch(e => cb(e))
|
|
419
459
|
})
|
|
420
460
|
}
|
|
421
461
|
|
package/lib/plugins.js
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
// support server-side plugins
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// forward
|
|
11
|
+
import fs from 'node:fs'
|
|
12
|
+
import { pathToFileURL } from 'node:url'
|
|
13
|
+
// import forward from './forward.cjs'; // Uncomment if needed and adjust import style if it's not a default export
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
export default argv => {
|
|
16
16
|
// NOTE: plugins are now in their own package directories alongside this one...
|
|
17
17
|
// Plugins are in directories of the form wiki-package-*
|
|
18
18
|
// those with a server component will have a server directory
|
|
@@ -39,11 +39,8 @@ module.exports = exports = argv => {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const startServers = params => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// params.emitter = emitter
|
|
45
|
-
|
|
46
|
-
Object.keys(require.main.require('./package').dependencies)
|
|
42
|
+
const dependencies = params.packageJson.dependencies
|
|
43
|
+
Object.keys(dependencies)
|
|
47
44
|
.filter(depend => depend.startsWith('wiki-plugin'))
|
|
48
45
|
.forEach(plugin => {
|
|
49
46
|
startServer(params, plugin)
|
package/lib/random_id.js
CHANGED
|
@@ -10,6 +10,4 @@
|
|
|
10
10
|
// Simple random hex generator, takes an optional number of
|
|
11
11
|
// chars that defaults to 16 and returns a random id.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
module.exports = random_id.random_id = random_id
|
|
13
|
+
export default (chars = 16) => [...Array(chars)].map(() => Math.floor(Math.random() * 16).toString(16)).join('')
|
package/lib/search.js
CHANGED
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
// **search.js**
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
import fs from 'node:fs'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import events from 'node:events'
|
|
14
|
+
import url from 'node:url'
|
|
15
|
+
import writeFileAtomic from 'write-file-atomic'
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
import miniSearch from 'minisearch'
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
export default argv => {
|
|
20
20
|
const wikiName = new URL(argv.url).hostname
|
|
21
21
|
let siteIndex = []
|
|
22
22
|
const queue = []
|
|
@@ -169,8 +169,12 @@ module.exports = exports = argv => {
|
|
|
169
169
|
.replace(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g, ' ')
|
|
170
170
|
.replace(/<(?:[^>])+>/g, ' ')
|
|
171
171
|
.replace(/(https?:.*?)(?=\p{White_Space}|\p{Quotation_Mark}|$)/gu, match => {
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
try {
|
|
173
|
+
const myUrl = new URL(match)
|
|
174
|
+
return myUrl.hostname
|
|
175
|
+
} catch {
|
|
176
|
+
return ' '
|
|
177
|
+
}
|
|
174
178
|
})
|
|
175
179
|
.replace(/[\p{P}\p{Emoji}\p{Symbol}}]+/gu, ' ')
|
|
176
180
|
.replace(/[\p{White_Space}\n\t]+/gu, ' ')
|
package/lib/security.js
CHANGED
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
// allow the server to run read-only.
|
|
14
14
|
|
|
15
15
|
// #### Requires ####
|
|
16
|
-
|
|
16
|
+
import fs from 'node:fs'
|
|
17
17
|
|
|
18
18
|
// Export a function that generates security handler
|
|
19
19
|
// when called with options object.
|
|
20
|
-
|
|
20
|
+
export default (log, loga, argv) => {
|
|
21
21
|
const security = {}
|
|
22
22
|
|
|
23
23
|
// #### Private utility methods. ####
|
|
@@ -84,7 +84,16 @@ module.exports = exports = (log, loga, argv) => {
|
|
|
84
84
|
}
|
|
85
85
|
// Wiki server admin
|
|
86
86
|
security.isAdmin = () => {
|
|
87
|
-
|
|
87
|
+
// nobody is admin - unless legacy support, and test
|
|
88
|
+
if (argv.security_legacy) {
|
|
89
|
+
if (argv.test) {
|
|
90
|
+
return true
|
|
91
|
+
} else {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
88
97
|
}
|
|
89
98
|
security.defineRoutes = (app, cors, updateOwner) => {
|
|
90
99
|
// default security does not have any routes
|
package/lib/server.js
CHANGED
|
@@ -19,20 +19,18 @@
|
|
|
19
19
|
// npm install
|
|
20
20
|
|
|
21
21
|
// Standard lib
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const { pipeline } = require('node:stream/promises')
|
|
22
|
+
import fs from 'fs'
|
|
23
|
+
import path from 'path'
|
|
24
|
+
import url from 'url'
|
|
25
|
+
import { pipeline } from 'node:stream/promises'
|
|
27
26
|
|
|
28
27
|
// From npm
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const f = require('flates')
|
|
28
|
+
import express from 'express'
|
|
29
|
+
import hbs from 'express-hbs'
|
|
30
|
+
import f from 'flates'
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
import createDOMPurify from 'dompurify'
|
|
33
|
+
import { JSDOM } from 'jsdom'
|
|
36
34
|
|
|
37
35
|
const window = new JSDOM('').window
|
|
38
36
|
const DOMPurify = createDOMPurify(window)
|
|
@@ -40,21 +38,53 @@ const DOMPurify = createDOMPurify(window)
|
|
|
40
38
|
// Using native fetch API (available in Node.js 18+)
|
|
41
39
|
|
|
42
40
|
// Express 4 middleware
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// session = require
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
import logger from 'morgan'
|
|
42
|
+
import cookieParser from 'cookie-parser'
|
|
43
|
+
import methodOverride from 'method-override'
|
|
44
|
+
// session = require('express-session') // This one was commented out — uncomment if used
|
|
45
|
+
import sessions from 'client-sessions'
|
|
46
|
+
import bodyParser from 'body-parser'
|
|
47
|
+
import errorHandler from 'errorhandler'
|
|
50
48
|
|
|
51
49
|
// Local files
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
// Make sure these files are ESM modules or compatible
|
|
51
|
+
// If they are CommonJS, you will need to dynamically import them (see below)
|
|
52
|
+
import defargs from './defaultargs.js'
|
|
53
|
+
import resolveClient from 'wiki-client/lib/resolve.js'
|
|
54
|
+
import pluginsFactory from './plugins.js'
|
|
55
|
+
import sitemapFactory from './sitemap.js'
|
|
56
|
+
import searchFactory from './search.js'
|
|
57
|
+
import { warn } from 'node:console'
|
|
58
|
+
import { createRequire } from 'module'
|
|
59
|
+
const require = createRequire(import.meta.url)
|
|
60
|
+
|
|
61
|
+
// Use import to load package.json from the main application's working directory
|
|
62
|
+
//const { default: packageJson } = await import('wiki/package.json', { with: { type: 'json' } })
|
|
63
|
+
const wikiPackageImport = async () => {
|
|
64
|
+
let done = false
|
|
65
|
+
return new Promise(resolve => {
|
|
66
|
+
import('wiki/package.json', { with: { type: 'json' } })
|
|
67
|
+
.then(imported => {
|
|
68
|
+
done = true
|
|
69
|
+
resolve(imported.default)
|
|
70
|
+
})
|
|
71
|
+
.catch(e => {
|
|
72
|
+
return e
|
|
73
|
+
})
|
|
74
|
+
.then(async () => {
|
|
75
|
+
if (done) return
|
|
76
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json')
|
|
77
|
+
const packageJsonUrl = url.pathToFileURL(packageJsonPath).href
|
|
78
|
+
import(packageJsonUrl, { with: { type: 'json' } })
|
|
79
|
+
.then(imported => {
|
|
80
|
+
resolve(imported.default)
|
|
81
|
+
})
|
|
82
|
+
.catch(e => console.error('problems importing package', e))
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const packageJson = await wikiPackageImport()
|
|
58
88
|
|
|
59
89
|
const render = page => {
|
|
60
90
|
return (
|
|
@@ -91,7 +121,7 @@ const render = page => {
|
|
|
91
121
|
)
|
|
92
122
|
}
|
|
93
123
|
// Set export objects for node and coffee to a function that generates a sfw server.
|
|
94
|
-
|
|
124
|
+
export default async argv => {
|
|
95
125
|
// Create the main application object, app.
|
|
96
126
|
const app = express()
|
|
97
127
|
|
|
@@ -105,6 +135,8 @@ module.exports = exports = argv => {
|
|
|
105
135
|
|
|
106
136
|
app.startOpts = argv
|
|
107
137
|
|
|
138
|
+
const wikiName = new URL(argv.url).hostname
|
|
139
|
+
|
|
108
140
|
const log = (...stuff) => {
|
|
109
141
|
if (argv.debug) console.log(stuff)
|
|
110
142
|
}
|
|
@@ -126,18 +158,23 @@ module.exports = exports = argv => {
|
|
|
126
158
|
}
|
|
127
159
|
next()
|
|
128
160
|
}
|
|
161
|
+
|
|
129
162
|
let pagehandler, sitemaphandler, searchhandler, securityhandler
|
|
130
|
-
// Require the database adapter and initialize it with options.
|
|
131
|
-
app.pagehandler = pagehandler = require(argv.database.type)(argv)
|
|
132
163
|
|
|
133
|
-
//
|
|
164
|
+
// Dynamically import database adapter (since the module name is dynamic)
|
|
165
|
+
const dbModule = await import(argv.database.type)
|
|
166
|
+
app.pagehandler = pagehandler = dbModule.default(argv)
|
|
167
|
+
|
|
168
|
+
// Initialize sitemap handler
|
|
134
169
|
app.sitemaphandler = sitemaphandler = sitemapFactory(argv)
|
|
135
170
|
|
|
136
|
-
//
|
|
171
|
+
// Initialize search handler
|
|
137
172
|
app.searchhandler = searchhandler = searchFactory(argv)
|
|
138
173
|
|
|
139
|
-
//
|
|
140
|
-
|
|
174
|
+
// Dynamically import security adapter (also dynamic)
|
|
175
|
+
console.log('security_type', argv.security_type)
|
|
176
|
+
const securityModule = await import(argv.security_type)
|
|
177
|
+
app.securityhandler = securityhandler = securityModule.default(log, loga, argv)
|
|
141
178
|
|
|
142
179
|
// If the site is owned, owner will contain the name of the owner
|
|
143
180
|
let owner = ''
|
|
@@ -220,7 +257,7 @@ module.exports = exports = argv => {
|
|
|
220
257
|
// use logger, at least in development, probably needs a param to configure (or turn off).
|
|
221
258
|
// use stream to direct to somewhere other than stdout.
|
|
222
259
|
logger.token('vhost', (req, res) => {
|
|
223
|
-
return colorString(
|
|
260
|
+
return colorString(wikiName)
|
|
224
261
|
})
|
|
225
262
|
app.use(logger(':vhost :method :url :status :res[content-length] - :response-time ms'))
|
|
226
263
|
app.use(cookieParser())
|
|
@@ -269,13 +306,10 @@ module.exports = exports = argv => {
|
|
|
269
306
|
app.use('/assets', cors, express.static(argv.assets))
|
|
270
307
|
|
|
271
308
|
// Add static routes to the plugins client.
|
|
272
|
-
Object.keys(
|
|
309
|
+
Object.keys(packageJson.dependencies)
|
|
273
310
|
.filter(depend => depend.startsWith('wiki-plugin'))
|
|
274
311
|
.forEach(plugin => {
|
|
275
|
-
const clientPath = path.join(
|
|
276
|
-
path.dirname(require.resolve(`${plugin}/package`, { paths: require.main.paths })),
|
|
277
|
-
'client',
|
|
278
|
-
)
|
|
312
|
+
const clientPath = path.join(path.dirname(require.resolve(`${plugin}/package`)), 'client')
|
|
279
313
|
const pluginPath = '/plugins/' + plugin.slice(12)
|
|
280
314
|
app.use(pluginPath, cors, express.static(clientPath, staticPathOptions))
|
|
281
315
|
})
|
|
@@ -388,17 +422,28 @@ module.exports = exports = argv => {
|
|
|
388
422
|
app.get('/system/factories.json', (req, res) => {
|
|
389
423
|
res.status(200)
|
|
390
424
|
res.header('Content-Type', 'application/json')
|
|
425
|
+
|
|
391
426
|
const factories = []
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
427
|
+
|
|
428
|
+
const getPackageFactory = plugin => {
|
|
429
|
+
return new Promise(resolve => {
|
|
430
|
+
import(`${plugin}/factory.json`, { with: { type: 'json' } })
|
|
431
|
+
.then(({ default: factory }) => {
|
|
432
|
+
resolve(factories.push(factory))
|
|
433
|
+
})
|
|
434
|
+
.catch(() => {
|
|
435
|
+
resolve()
|
|
436
|
+
})
|
|
400
437
|
})
|
|
401
|
-
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
Promise.all(
|
|
441
|
+
Object.keys(packageJson.dependencies)
|
|
442
|
+
.filter(depend => depend.startsWith('wiki-plugin'))
|
|
443
|
+
.map(plugin => {
|
|
444
|
+
return getPackageFactory(plugin)
|
|
445
|
+
}),
|
|
446
|
+
).then(() => res.end(JSON.stringify(factories)))
|
|
402
447
|
})
|
|
403
448
|
|
|
404
449
|
// ###### Json Routes ######
|
|
@@ -452,7 +497,7 @@ module.exports = exports = argv => {
|
|
|
452
497
|
if (!err) {
|
|
453
498
|
res.sendFile(favLoc, { dotfiles: 'allow' })
|
|
454
499
|
} else {
|
|
455
|
-
res.sendFile(defaultFavLoc)
|
|
500
|
+
res.sendFile(defaultFavLoc, { dotfiles: 'allow' })
|
|
456
501
|
}
|
|
457
502
|
})
|
|
458
503
|
})
|
|
@@ -511,32 +556,34 @@ module.exports = exports = argv => {
|
|
|
511
556
|
// Send an array of pages currently in the recycler via json
|
|
512
557
|
app.get('/recycler/system/slugs.json', authorized, (req, res) => {
|
|
513
558
|
fs.readdir(argv.recycler, (e, files) => {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
559
|
+
if (e) {
|
|
560
|
+
return res.e(e)
|
|
561
|
+
}
|
|
562
|
+
const doRecyclermap = async file => {
|
|
563
|
+
return new Promise(resolve => {
|
|
564
|
+
const recycleFile = 'recycler/' + file
|
|
565
|
+
pagehandler.get(recycleFile, (e, page, status) => {
|
|
566
|
+
if (e || status === 404) {
|
|
567
|
+
console.log('Problem building recycler map:', file, 'e: ', e)
|
|
568
|
+
// this will leave an undefined/empty item in the array, which we will filter out later
|
|
569
|
+
return resolve(null)
|
|
570
|
+
}
|
|
571
|
+
resolve({
|
|
572
|
+
slug: file,
|
|
573
|
+
title: page.title,
|
|
574
|
+
})
|
|
525
575
|
})
|
|
526
576
|
})
|
|
527
577
|
}
|
|
528
578
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
recyclermap = recyclermap.filter(el => !!el)
|
|
538
|
-
res.send(recyclermap)
|
|
539
|
-
})
|
|
579
|
+
Promise.all(files.map(doRecyclermap))
|
|
580
|
+
.then(recyclermap => {
|
|
581
|
+
recyclermap = recyclermap.filter(el => !!el)
|
|
582
|
+
res.send(recyclermap)
|
|
583
|
+
})
|
|
584
|
+
.catch(error => {
|
|
585
|
+
res.e(error)
|
|
586
|
+
})
|
|
540
587
|
})
|
|
541
588
|
})
|
|
542
589
|
|
|
@@ -642,28 +689,29 @@ module.exports = exports = argv => {
|
|
|
642
689
|
if (e) {
|
|
643
690
|
return res.e(e)
|
|
644
691
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
(stub, done) => {
|
|
692
|
+
const pagePromises = sitemap.map(stub => {
|
|
693
|
+
return new Promise((resolve, reject) => {
|
|
648
694
|
pagehandler.get(stub.slug, (error, page) => {
|
|
649
|
-
if (
|
|
650
|
-
return
|
|
695
|
+
if (error) {
|
|
696
|
+
return reject(error)
|
|
651
697
|
}
|
|
652
|
-
|
|
698
|
+
resolve({ slug: stub.slug, page })
|
|
653
699
|
})
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
)
|
|
665
|
-
}
|
|
666
|
-
|
|
700
|
+
})
|
|
701
|
+
})
|
|
702
|
+
|
|
703
|
+
Promise.all(pagePromises)
|
|
704
|
+
.then(pages => {
|
|
705
|
+
const pageExport = pages.reduce((dict, combined) => {
|
|
706
|
+
dict[combined.slug] = combined.page
|
|
707
|
+
return dict
|
|
708
|
+
}, {})
|
|
709
|
+
// TODO: this fails for a very large site
|
|
710
|
+
res.json(pageExport)
|
|
711
|
+
})
|
|
712
|
+
.catch(error => {
|
|
713
|
+
res.e(error)
|
|
714
|
+
})
|
|
667
715
|
})
|
|
668
716
|
})
|
|
669
717
|
|
|
@@ -677,27 +725,54 @@ module.exports = exports = argv => {
|
|
|
677
725
|
}
|
|
678
726
|
|
|
679
727
|
app.get('/system/version.json', admin, (req, res) => {
|
|
728
|
+
const getPackageVersion = packageName => {
|
|
729
|
+
return new Promise(resolve => {
|
|
730
|
+
try {
|
|
731
|
+
// Use import to load package.json from the main application's working directory
|
|
732
|
+
import(`${packageName}/package.json`, { with: { type: 'json' } }).then(({ default: packageJson }) => {
|
|
733
|
+
resolve({ [packageName]: packageJson.version })
|
|
734
|
+
})
|
|
735
|
+
} catch (error) {
|
|
736
|
+
console.error(`Error reading package for ${packageName}:`, error)
|
|
737
|
+
resolve({ [packageName]: 'unknown' })
|
|
738
|
+
}
|
|
739
|
+
})
|
|
740
|
+
}
|
|
741
|
+
|
|
680
742
|
const versions = {}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
743
|
+
|
|
744
|
+
const security = () => {
|
|
745
|
+
return new Promise(resolve => {
|
|
746
|
+
Promise.all(
|
|
747
|
+
Object.keys(packageJson.dependencies)
|
|
748
|
+
.filter(depend => depend.startsWith('wiki-security'))
|
|
749
|
+
.map(key => {
|
|
750
|
+
return getPackageVersion(key)
|
|
751
|
+
}),
|
|
752
|
+
).then(values => {
|
|
753
|
+
resolve({ security: values.reduce((acc, cV) => Object.assign(acc, cV), {}) })
|
|
754
|
+
})
|
|
691
755
|
})
|
|
756
|
+
}
|
|
692
757
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
758
|
+
const plugins = () => {
|
|
759
|
+
return new Promise(resolve => {
|
|
760
|
+
Promise.all(
|
|
761
|
+
Object.keys(packageJson.dependencies)
|
|
762
|
+
.filter(depend => depend.startsWith('wiki-plugin'))
|
|
763
|
+
.map(key => {
|
|
764
|
+
return getPackageVersion(key)
|
|
765
|
+
}),
|
|
766
|
+
).then(values => {
|
|
767
|
+
resolve({ plugins: values.reduce((acc, cV) => Object.assign(acc, cV), {}) })
|
|
768
|
+
})
|
|
698
769
|
})
|
|
770
|
+
}
|
|
699
771
|
|
|
700
|
-
|
|
772
|
+
Promise.all([getPackageVersion('wiki-server'), getPackageVersion('wiki-client'), security(), plugins()]).then(v => {
|
|
773
|
+
Object.assign(versions, { [packageJson.name]: packageJson.version }, ...v)
|
|
774
|
+
res.json(versions)
|
|
775
|
+
})
|
|
701
776
|
})
|
|
702
777
|
|
|
703
778
|
// ##### Proxy routes #####
|
|
@@ -914,7 +989,7 @@ module.exports = exports = argv => {
|
|
|
914
989
|
// ### Plugins ###
|
|
915
990
|
// Should replace most WebSocketServers below.
|
|
916
991
|
const plugins = pluginsFactory(argv)
|
|
917
|
-
plugins.startServers({ argv, app })
|
|
992
|
+
plugins.startServers({ argv, app, packageJson })
|
|
918
993
|
// ### Sitemap ###
|
|
919
994
|
// create sitemap at start-up
|
|
920
995
|
sitemaphandler.createSitemap(pagehandler)
|
package/lib/sitemap.js
CHANGED
|
@@ -7,14 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
// **sitemap.coffee**
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
import path from 'node:path'
|
|
12
|
+
import events from 'node:events'
|
|
13
|
+
import writeFileAtomic from 'write-file-atomic'
|
|
14
|
+
import xml2js from 'xml2js'
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
const path = require('path')
|
|
13
|
-
const events = require('events')
|
|
14
|
-
const writeFileAtomic = require('write-file-atomic')
|
|
15
|
-
const xml2js = require('xml2js')
|
|
16
|
-
|
|
17
|
-
const synopsis = require('wiki-client/lib/synopsis')
|
|
16
|
+
import synopsis from 'wiki-client/lib/synopsis.js' // Add .js if needed
|
|
18
17
|
|
|
19
18
|
const asSlug = name =>
|
|
20
19
|
name
|
|
@@ -22,7 +21,7 @@ const asSlug = name =>
|
|
|
22
21
|
.replace(/[^A-Za-z0-9-]/g, '')
|
|
23
22
|
.toLowerCase()
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
export default argv => {
|
|
26
25
|
const wikiName = new URL(argv.url).hostname
|
|
27
26
|
|
|
28
27
|
let sitemap = []
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-server",
|
|
3
3
|
"description": "A Federated Wiki Server",
|
|
4
|
-
"version": "0.26.0-rc.
|
|
4
|
+
"version": "0.26.0-rc.11",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"author": {
|
|
6
7
|
"name": "Ward Cunningham",
|
|
7
8
|
"email": "ward@c2.com",
|
|
@@ -29,38 +30,39 @@
|
|
|
29
30
|
"Andrew Shell <andrew@andrewshell.org>",
|
|
30
31
|
"jon r <jon@allmende.io>"
|
|
31
32
|
],
|
|
33
|
+
"main": "index.js",
|
|
32
34
|
"dependencies": {
|
|
33
|
-
"
|
|
34
|
-
"body-parser": "^2.2.0",
|
|
35
|
+
"body-parser": "^2.2.1",
|
|
35
36
|
"client-sessions": "^0.8.0",
|
|
36
37
|
"coffeescript": "^2.5.0",
|
|
37
38
|
"cookie-parser": "^1.4.4",
|
|
38
|
-
"dompurify": "^3.
|
|
39
|
+
"dompurify": "^3.3.0",
|
|
39
40
|
"errorhandler": "^1.5.1",
|
|
40
41
|
"express": "^5.1.0",
|
|
41
42
|
"express-hbs": "^2.5.0",
|
|
42
43
|
"flates": "0.0.5",
|
|
43
|
-
"jsdom": "^
|
|
44
|
+
"jsdom": "^27.2.0",
|
|
44
45
|
"method-override": "^3.0.0",
|
|
45
|
-
"minisearch": "^7.
|
|
46
|
-
"morgan": "^1.
|
|
47
|
-
"write-file-atomic": "^
|
|
46
|
+
"minisearch": "^7.2.0",
|
|
47
|
+
"morgan": "^1.10.1",
|
|
48
|
+
"write-file-atomic": "^7.0.0",
|
|
48
49
|
"xml2js": "^0.6.2"
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
51
52
|
"prettier:format": "prettier --write './**/*.js'",
|
|
52
53
|
"prettier:check": "prettier --check ./**/*.js",
|
|
53
|
-
"test": "node --test",
|
|
54
|
+
"test": "cd test; node --test",
|
|
55
|
+
"watch": "cd test; node --test --watch",
|
|
54
56
|
"update-authors": "node scripts/update-authors.js"
|
|
55
57
|
},
|
|
56
58
|
"devDependencies": {
|
|
57
|
-
"@eslint/js": "^9.
|
|
58
|
-
"eslint": "^9.
|
|
59
|
-
"globals": "^16.
|
|
59
|
+
"@eslint/js": "^9.39.1",
|
|
60
|
+
"eslint": "^9.39.1",
|
|
61
|
+
"globals": "^16.5.0",
|
|
60
62
|
"grunt-git-authors": "^3.2.0",
|
|
61
|
-
"prettier": "^3.
|
|
62
|
-
"supertest": "^7.
|
|
63
|
-
"wiki-client": "^0.31.
|
|
63
|
+
"prettier": "^3.7.3",
|
|
64
|
+
"supertest": "^7.1.4",
|
|
65
|
+
"wiki-client": "^0.31.6",
|
|
64
66
|
"wiki-plugin-activity": "0.7",
|
|
65
67
|
"wiki-plugin-video": "^0.4"
|
|
66
68
|
},
|