solid-server 5.6.9-beta

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.
Files changed (170) hide show
  1. package/.acl +10 -0
  2. package/.github/workflows/ci.yml +47 -0
  3. package/.nvmrc +1 -0
  4. package/.snyk +35 -0
  5. package/.well-known/.acl +15 -0
  6. package/CHANGELOG.md +198 -0
  7. package/CONTRIBUTING.md +139 -0
  8. package/CONTRIBUTORS.md +36 -0
  9. package/Dockerfile +22 -0
  10. package/LICENSE.md +23 -0
  11. package/README.md +453 -0
  12. package/bin/lib/cli-utils.js +85 -0
  13. package/bin/lib/cli.js +39 -0
  14. package/bin/lib/init.js +94 -0
  15. package/bin/lib/invalidUsernames.js +148 -0
  16. package/bin/lib/migrateLegacyResources.js +69 -0
  17. package/bin/lib/options.js +399 -0
  18. package/bin/lib/start.js +148 -0
  19. package/bin/lib/updateIndex.js +56 -0
  20. package/bin/solid +3 -0
  21. package/bin/solid-test +12 -0
  22. package/bin/solid.js +3 -0
  23. package/common/css/solid.css +58 -0
  24. package/common/fonts/glyphicons-halflings-regular.eot +0 -0
  25. package/common/fonts/glyphicons-halflings-regular.svg +288 -0
  26. package/common/fonts/glyphicons-halflings-regular.ttf +0 -0
  27. package/common/fonts/glyphicons-halflings-regular.woff +0 -0
  28. package/common/fonts/glyphicons-halflings-regular.woff2 +0 -0
  29. package/common/img/.gitkeep +0 -0
  30. package/common/js/auth-buttons.js +65 -0
  31. package/common/js/solid.js +454 -0
  32. package/common/well-known/security.txt +2 -0
  33. package/config/defaults.js +25 -0
  34. package/config/usernames-blacklist.json +4 -0
  35. package/config.json-default +22 -0
  36. package/default-templates/emails/delete-account.js +49 -0
  37. package/default-templates/emails/invalid-username.js +30 -0
  38. package/default-templates/emails/reset-password.js +49 -0
  39. package/default-templates/emails/welcome.js +39 -0
  40. package/default-templates/new-account/.acl +26 -0
  41. package/default-templates/new-account/.meta +5 -0
  42. package/default-templates/new-account/.meta.acl +25 -0
  43. package/default-templates/new-account/.well-known/.acl +19 -0
  44. package/default-templates/new-account/favicon.ico +0 -0
  45. package/default-templates/new-account/favicon.ico.acl +26 -0
  46. package/default-templates/new-account/inbox/.acl +26 -0
  47. package/default-templates/new-account/private/.acl +10 -0
  48. package/default-templates/new-account/profile/.acl +19 -0
  49. package/default-templates/new-account/profile/card$.ttl +25 -0
  50. package/default-templates/new-account/public/.acl +19 -0
  51. package/default-templates/new-account/robots.txt +3 -0
  52. package/default-templates/new-account/robots.txt.acl +26 -0
  53. package/default-templates/new-account/settings/.acl +20 -0
  54. package/default-templates/new-account/settings/prefs.ttl +15 -0
  55. package/default-templates/new-account/settings/privateTypeIndex.ttl +4 -0
  56. package/default-templates/new-account/settings/publicTypeIndex.ttl +4 -0
  57. package/default-templates/new-account/settings/publicTypeIndex.ttl.acl +25 -0
  58. package/default-templates/new-account/settings/serverSide.ttl.acl +13 -0
  59. package/default-templates/new-account/settings/serverSide.ttl.inactive +12 -0
  60. package/default-templates/server/.acl +10 -0
  61. package/default-templates/server/.well-known/.acl +15 -0
  62. package/default-templates/server/favicon.ico +0 -0
  63. package/default-templates/server/favicon.ico.acl +15 -0
  64. package/default-templates/server/index.html +55 -0
  65. package/default-templates/server/robots.txt +3 -0
  66. package/default-templates/server/robots.txt.acl +15 -0
  67. package/default-views/account/account-deleted.hbs +17 -0
  68. package/default-views/account/delete-confirm.hbs +51 -0
  69. package/default-views/account/delete-link-sent.hbs +17 -0
  70. package/default-views/account/delete.hbs +51 -0
  71. package/default-views/account/invalid-username.hbs +22 -0
  72. package/default-views/account/register-disabled.hbs +6 -0
  73. package/default-views/account/register-form.hbs +132 -0
  74. package/default-views/account/register.hbs +24 -0
  75. package/default-views/auth/auth-hidden-fields.hbs +8 -0
  76. package/default-views/auth/change-password.hbs +58 -0
  77. package/default-views/auth/goodbye.hbs +23 -0
  78. package/default-views/auth/login-required.hbs +34 -0
  79. package/default-views/auth/login-tls.hbs +11 -0
  80. package/default-views/auth/login-username-password.hbs +28 -0
  81. package/default-views/auth/login.hbs +55 -0
  82. package/default-views/auth/no-permission.hbs +29 -0
  83. package/default-views/auth/password-changed.hbs +27 -0
  84. package/default-views/auth/reset-link-sent.hbs +21 -0
  85. package/default-views/auth/reset-password.hbs +52 -0
  86. package/default-views/auth/sharing.hbs +49 -0
  87. package/default-views/shared/create-account.hbs +8 -0
  88. package/default-views/shared/error.hbs +5 -0
  89. package/docs/how-to-delete-your-account.md +56 -0
  90. package/docs/login-and-grant-access-to-application.md +32 -0
  91. package/examples/custom-error-handling.js +31 -0
  92. package/examples/ldp-with-webid.js +12 -0
  93. package/examples/simple-express-app.js +20 -0
  94. package/examples/simple-ldp-server.js +8 -0
  95. package/favicon.ico +0 -0
  96. package/favicon.ico.acl +15 -0
  97. package/index.html +48 -0
  98. package/index.js +3 -0
  99. package/lib/acl-checker.js +274 -0
  100. package/lib/api/accounts/user-accounts.js +88 -0
  101. package/lib/api/authn/force-user.js +21 -0
  102. package/lib/api/authn/index.js +5 -0
  103. package/lib/api/authn/webid-oidc.js +202 -0
  104. package/lib/api/authn/webid-tls.js +69 -0
  105. package/lib/api/index.js +6 -0
  106. package/lib/capability-discovery.js +54 -0
  107. package/lib/common/fs-utils.js +43 -0
  108. package/lib/common/template-utils.js +50 -0
  109. package/lib/common/user-utils.js +28 -0
  110. package/lib/create-app.js +322 -0
  111. package/lib/create-server.js +107 -0
  112. package/lib/debug.js +17 -0
  113. package/lib/handlers/allow.js +82 -0
  114. package/lib/handlers/auth-proxy.js +63 -0
  115. package/lib/handlers/copy.js +39 -0
  116. package/lib/handlers/cors-proxy.js +95 -0
  117. package/lib/handlers/delete.js +23 -0
  118. package/lib/handlers/error-pages.js +212 -0
  119. package/lib/handlers/get.js +219 -0
  120. package/lib/handlers/index.js +42 -0
  121. package/lib/handlers/options.js +33 -0
  122. package/lib/handlers/patch/n3-patch-parser.js +49 -0
  123. package/lib/handlers/patch/sparql-update-parser.js +16 -0
  124. package/lib/handlers/patch.js +203 -0
  125. package/lib/handlers/post.js +99 -0
  126. package/lib/handlers/put.js +56 -0
  127. package/lib/handlers/restrict-to-top-domain.js +13 -0
  128. package/lib/header.js +136 -0
  129. package/lib/http-error.js +34 -0
  130. package/lib/ldp-container.js +161 -0
  131. package/lib/ldp-copy.js +73 -0
  132. package/lib/ldp-middleware.js +32 -0
  133. package/lib/ldp.js +620 -0
  134. package/lib/lock.js +10 -0
  135. package/lib/metadata.js +10 -0
  136. package/lib/models/account-manager.js +603 -0
  137. package/lib/models/account-template.js +152 -0
  138. package/lib/models/authenticator.js +333 -0
  139. package/lib/models/oidc-manager.js +53 -0
  140. package/lib/models/solid-host.js +131 -0
  141. package/lib/models/user-account.js +112 -0
  142. package/lib/models/webid-tls-certificate.js +184 -0
  143. package/lib/payment-pointer-discovery.js +83 -0
  144. package/lib/requests/add-cert-request.js +138 -0
  145. package/lib/requests/auth-request.js +234 -0
  146. package/lib/requests/create-account-request.js +468 -0
  147. package/lib/requests/delete-account-confirm-request.js +170 -0
  148. package/lib/requests/delete-account-request.js +144 -0
  149. package/lib/requests/login-request.js +205 -0
  150. package/lib/requests/password-change-request.js +201 -0
  151. package/lib/requests/password-reset-email-request.js +199 -0
  152. package/lib/requests/sharing-request.js +259 -0
  153. package/lib/resource-mapper.js +198 -0
  154. package/lib/server-config.js +167 -0
  155. package/lib/services/blacklist-service.js +33 -0
  156. package/lib/services/email-service.js +162 -0
  157. package/lib/services/token-service.js +47 -0
  158. package/lib/utils.js +254 -0
  159. package/lib/webid/index.js +13 -0
  160. package/lib/webid/lib/get.js +27 -0
  161. package/lib/webid/lib/parse.js +12 -0
  162. package/lib/webid/tls/index.js +185 -0
  163. package/package.json +172 -0
  164. package/renovate.json +5 -0
  165. package/robots.txt +3 -0
  166. package/robots.txt.acl +15 -0
  167. package/static/account-recovery.html +78 -0
  168. package/static/popup-redirect.html +1 -0
  169. package/static/signup.html +108 -0
  170. package/static/signup.html.acl +14 -0
@@ -0,0 +1,148 @@
1
+ const fs = require('fs-extra')
2
+ const Handlebars = require('handlebars')
3
+ const path = require('path')
4
+
5
+ const { getAccountManager, loadConfig, loadUsernames } = require('./cli-utils')
6
+ const { isValidUsername } = require('../../lib/common/user-utils')
7
+ const blacklistService = require('../../lib/services/blacklist-service')
8
+ const { initConfigDir, initTemplateDirs } = require('../../lib/server-config')
9
+ const { fromServerConfig } = require('../../lib/models/oidc-manager')
10
+
11
+ const EmailService = require('../../lib/services/email-service')
12
+ const SolidHost = require('../../lib/models/solid-host')
13
+
14
+ module.exports = function (program) {
15
+ program
16
+ .command('invalidusernames')
17
+ .option('--notify', 'Will notify users with usernames that are invalid')
18
+ .option('--delete', 'Will delete users with usernames that are invalid')
19
+ .description('Manage usernames that are invalid')
20
+ .action(async (options) => {
21
+ const config = loadConfig(program, options)
22
+ if (!config.multiuser) {
23
+ return console.error('You are running a single user server, no need to check for invalid usernames')
24
+ }
25
+
26
+ const invalidUsernames = getInvalidUsernames(config)
27
+ const host = SolidHost.from({ port: config.port, serverUri: config.serverUri })
28
+ const accountManager = getAccountManager(config, { host })
29
+
30
+ if (options.notify) {
31
+ return notifyUsers(invalidUsernames, accountManager, config)
32
+ }
33
+
34
+ if (options.delete) {
35
+ return deleteUsers(invalidUsernames, accountManager, config, host)
36
+ }
37
+
38
+ listUsernames(invalidUsernames)
39
+ })
40
+ }
41
+
42
+ function backupIndexFile (username, accountManager, invalidUsernameTemplate, dateOfRemoval, supportEmail) {
43
+ const userDirectory = accountManager.accountDirFor(username)
44
+ const currentIndex = path.join(userDirectory, 'index.html')
45
+ const currentIndexExists = fs.existsSync(currentIndex)
46
+ const backupIndex = path.join(userDirectory, 'index.backup.html')
47
+ const backupIndexExists = fs.existsSync(backupIndex)
48
+ if (currentIndexExists && !backupIndexExists) {
49
+ fs.renameSync(currentIndex, backupIndex)
50
+ createNewIndexAcl(userDirectory)
51
+ createNewIndex(username, invalidUsernameTemplate, dateOfRemoval, supportEmail, currentIndex)
52
+ console.info(`index.html updated for user ${username}`)
53
+ }
54
+ }
55
+
56
+ function createNewIndex (username, invalidUsernameTemplate, dateOfRemoval, supportEmail, currentIndex) {
57
+ const newIndexSource = invalidUsernameTemplate({
58
+ username,
59
+ dateOfRemoval,
60
+ supportEmail
61
+ })
62
+ fs.writeFileSync(currentIndex, newIndexSource, 'utf-8')
63
+ }
64
+
65
+ function createNewIndexAcl (userDirectory) {
66
+ const currentIndexAcl = path.join(userDirectory, 'index.html.acl')
67
+ const backupIndexAcl = path.join(userDirectory, 'index.backup.html.acl')
68
+ const currentIndexSource = fs.readFileSync(currentIndexAcl, 'utf-8')
69
+ const backupIndexSource = currentIndexSource.replace(/index.html/g, 'index.backup.html')
70
+ fs.writeFileSync(backupIndexAcl, backupIndexSource, 'utf-8')
71
+ }
72
+
73
+ async function deleteUsers (usernames, accountManager, config, host) {
74
+ const oidcManager = fromServerConfig({
75
+ ...config,
76
+ host
77
+ })
78
+ const deletingUsers = usernames
79
+ .map(async username => {
80
+ try {
81
+ const user = accountManager.userAccountFrom({ username })
82
+ await oidcManager.users.deleteUser(user)
83
+ } catch (error) {
84
+ if (error.message !== 'No email given') {
85
+ // 'No email given' is an expected error that we want to ignore
86
+ throw error
87
+ }
88
+ }
89
+ const userDirectory = accountManager.accountDirFor(username)
90
+ await fs.remove(userDirectory)
91
+ })
92
+ await Promise.all(deletingUsers)
93
+ console.info(`Deleted ${deletingUsers.length} users succeeded`)
94
+ }
95
+
96
+ function getInvalidUsernames (config) {
97
+ const usernames = loadUsernames(config)
98
+ return usernames.filter(username => !isValidUsername(username) || !blacklistService.validate(username))
99
+ }
100
+
101
+ function listUsernames (usernames) {
102
+ if (usernames.length === 0) {
103
+ return console.info('No invalid usernames was found')
104
+ }
105
+ console.info(`${usernames.length} invalid usernames were found:${usernames.map(username => `\n- ${username}`)}`)
106
+ }
107
+
108
+ async function notifyUsers (usernames, accountManager, config) {
109
+ const twoWeeksFromNow = Date.now() + 14 * 24 * 60 * 60 * 1000
110
+ const dateOfRemoval = (new Date(twoWeeksFromNow)).toLocaleDateString()
111
+ const { supportEmail } = config
112
+
113
+ updateIndexFiles(usernames, accountManager, dateOfRemoval, supportEmail)
114
+ await sendEmails(config, usernames, accountManager, dateOfRemoval, supportEmail)
115
+ }
116
+
117
+ async function sendEmails (config, usernames, accountManager, dateOfRemoval, supportEmail) {
118
+ if (config.email && config.email.host) {
119
+ const configPath = initConfigDir(config)
120
+ const templates = initTemplateDirs(configPath)
121
+ const users = await Promise.all(await usernames.map(async username => {
122
+ const emailAddress = await accountManager.loadAccountRecoveryEmail({ username })
123
+ const accountUri = accountManager.accountUriFor(username)
124
+ return { username, emailAddress, accountUri }
125
+ }))
126
+ const emailService = new EmailService(templates.email, config.email)
127
+ const sendingEmails = users
128
+ .filter(user => !!user.emailAddress)
129
+ .map(user => emailService.sendWithTemplate('invalid-username', {
130
+ to: user.emailAddress,
131
+ accountUri: user.accountUri,
132
+ dateOfRemoval,
133
+ supportEmail
134
+ }))
135
+ const emailsSent = await Promise.all(sendingEmails)
136
+ console.info(`${emailsSent.length} emails sent to users with invalid usernames`)
137
+ return
138
+ }
139
+ console.info('You have not configured an email service.')
140
+ console.info('Please set it up to send users email about their accounts')
141
+ }
142
+
143
+ function updateIndexFiles (usernames, accountManager, dateOfRemoval, supportEmail) {
144
+ const invalidUsernameFilePath = path.join(process.cwd(), 'default-views', 'account', 'invalid-username.hbs')
145
+ const source = fs.readFileSync(invalidUsernameFilePath, 'utf-8')
146
+ const invalidUsernameTemplate = Handlebars.compile(source)
147
+ usernames.forEach(username => backupIndexFile(username, accountManager, invalidUsernameTemplate, dateOfRemoval, supportEmail))
148
+ }
@@ -0,0 +1,69 @@
1
+ const fs = require('fs')
2
+ const Path = require('path')
3
+ const promisify = require('util').promisify
4
+ const readdir = promisify(fs.readdir)
5
+ const lstat = promisify(fs.lstat)
6
+ const rename = promisify(fs.rename)
7
+
8
+ /* Converts the old (pre-5.0.0) extensionless files to $-based files _with_ extensions
9
+ * to make them work in the new resource mapper (post-5.0.0).
10
+ * By default, all extensionless files (that used to be interpreted as Turtle) will now receive a '$.ttl' suffix. */
11
+ /* https://www.w3.org/DesignIssues/HTTPFilenameMapping.html */
12
+
13
+ module.exports = function (program) {
14
+ program
15
+ .command('migrate-legacy-resources')
16
+ .option('-p, --path <path>', 'Path to the data folder, defaults to \'data/\'')
17
+ .option('-s, --suffix <path>', 'The suffix to add to extensionless files, defaults to \'$.ttl\'')
18
+ .option('-v, --verbose', 'Path to the data folder')
19
+ .description('Migrate the data folder from node-solid-server 4 to node-solid-server 5')
20
+ .action(async (opts) => {
21
+ const verbose = opts.verbose
22
+ const suffix = opts.suffix || '$.ttl'
23
+ let paths = opts.path ? [opts.path] : ['data', 'config/templates']
24
+ paths = paths.map(path => path.startsWith(Path.sep) ? path : Path.join(process.cwd(), path))
25
+ try {
26
+ for (const path of paths) {
27
+ if (verbose) {
28
+ console.log(`Migrating files in ${path}`)
29
+ }
30
+ await migrate(path, suffix, verbose)
31
+ }
32
+ } catch (err) {
33
+ console.error(err)
34
+ }
35
+ })
36
+ }
37
+
38
+ async function migrate (path, suffix, verbose) {
39
+ const files = await readdir(path)
40
+ for (const file of files) {
41
+ const fullFilePath = Path.join(path, file)
42
+ const stat = await lstat(fullFilePath)
43
+ if (stat.isFile()) {
44
+ if (shouldMigrateFile(file)) {
45
+ const newFullFilePath = getNewFileName(fullFilePath, suffix)
46
+ if (verbose) {
47
+ console.log(`${fullFilePath}\n => ${newFullFilePath}`)
48
+ }
49
+ await rename(fullFilePath, newFullFilePath)
50
+ }
51
+ } else {
52
+ if (shouldMigrateFolder(file)) {
53
+ await migrate(fullFilePath, suffix, verbose)
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ function getNewFileName (fullFilePath, suffix) {
60
+ return fullFilePath + suffix
61
+ }
62
+
63
+ function shouldMigrateFile (filename) {
64
+ return filename.indexOf('.') < 0
65
+ }
66
+
67
+ function shouldMigrateFolder (foldername) {
68
+ return foldername[0] !== '.'
69
+ }
@@ -0,0 +1,399 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const validUrl = require('valid-url')
4
+ const { URL } = require('url')
5
+ const { isEmail } = require('validator')
6
+
7
+ module.exports = [
8
+ // {
9
+ // abbr: 'v',
10
+ // flag: true,
11
+ // help: 'Print the logs to console\n'
12
+ // },
13
+ {
14
+ name: 'root',
15
+ help: "Root folder to serve (default: './data')",
16
+ question: 'Path to the folder you want to serve. Default is',
17
+ default: './data',
18
+ prompt: true,
19
+ filter: (value) => path.resolve(value)
20
+ },
21
+ {
22
+ name: 'port',
23
+ help: 'SSL port to use',
24
+ question: 'SSL port to run on. Default is',
25
+ default: '8443',
26
+ prompt: true
27
+ },
28
+ {
29
+ name: 'server-uri',
30
+ question: 'Solid server uri (with protocol, hostname and port)',
31
+ help: "Solid server uri (default: 'https://localhost:8443')",
32
+ default: 'https://localhost:8443',
33
+ validate: validUri,
34
+ prompt: true
35
+ },
36
+ {
37
+ name: 'webid',
38
+ help: 'Enable WebID authentication and access control (uses HTTPS)',
39
+ flag: true,
40
+ default: true,
41
+ question: 'Enable WebID authentication',
42
+ prompt: true
43
+ },
44
+ {
45
+ name: 'mount',
46
+ help: "Serve on a specific URL path (default: '/')",
47
+ question: 'Serve Solid on URL path',
48
+ default: '/',
49
+ prompt: true
50
+ },
51
+ {
52
+ name: 'config-path',
53
+ question: 'Path to the config directory (for example: ./config)',
54
+ default: './config',
55
+ prompt: true
56
+ },
57
+ {
58
+ name: 'config-file',
59
+ question: 'Path to the config file (for example: ./config.json)',
60
+ default: './config.json',
61
+ prompt: true
62
+ },
63
+ {
64
+ name: 'db-path',
65
+ question: 'Path to the server metadata db directory (for users/apps etc)',
66
+ default: './.db',
67
+ prompt: true
68
+ },
69
+ {
70
+ name: 'auth',
71
+ help: 'Pick an authentication strategy for WebID: `tls` or `oidc`',
72
+ question: 'Select authentication strategy',
73
+ type: 'list',
74
+ choices: [
75
+ 'WebID-OpenID Connect'
76
+ ],
77
+ prompt: false,
78
+ default: 'WebID-OpenID Connect',
79
+ filter: (value) => {
80
+ if (value === 'WebID-OpenID Connect') return 'oidc'
81
+ },
82
+ when: (answers) => {
83
+ return answers.webid
84
+ }
85
+ },
86
+ {
87
+ name: 'use-owner',
88
+ question: 'Do you already have a WebID?',
89
+ type: 'confirm',
90
+ default: false,
91
+ hide: true
92
+ },
93
+ {
94
+ name: 'owner',
95
+ help: 'Set the owner of the storage (overwrites the root ACL file)',
96
+ question: 'Your webid (to overwrite the root ACL with)',
97
+ prompt: false,
98
+ validate: function (value) {
99
+ if (value === '' || !value.startsWith('http')) {
100
+ return 'Enter a valid Webid'
101
+ }
102
+ return true
103
+ },
104
+ when: function (answers) {
105
+ return answers['use-owner']
106
+ }
107
+ },
108
+ {
109
+ name: 'ssl-key',
110
+ help: 'Path to the SSL private key in PEM format',
111
+ validate: validPath,
112
+ prompt: true
113
+ },
114
+ {
115
+ name: 'ssl-cert',
116
+ help: 'Path to the SSL certificate key in PEM format',
117
+ validate: validPath,
118
+ prompt: true
119
+ },
120
+ {
121
+ name: 'no-reject-unauthorized',
122
+ help: 'Accept self-signed certificates',
123
+ flag: true,
124
+ default: false,
125
+ prompt: false
126
+ },
127
+ {
128
+ name: 'multiuser',
129
+ help: 'Enable multi-user mode',
130
+ question: 'Enable multi-user mode',
131
+ flag: true,
132
+ default: false,
133
+ prompt: true
134
+ },
135
+ {
136
+ name: 'idp',
137
+ help: 'Obsolete; use --multiuser',
138
+ prompt: false
139
+ },
140
+ {
141
+ name: 'no-live',
142
+ help: 'Disable live support through WebSockets',
143
+ flag: true,
144
+ default: false
145
+ },
146
+ // {
147
+ // full: 'default-app',
148
+ // help: 'URI to use as a default app for resources (default: https://linkeddata.github.io/warp/#/list/)'
149
+ // },
150
+ {
151
+ name: 'use-cors-proxy',
152
+ help: 'Do you want to have a CORS proxy endpoint?',
153
+ flag: true,
154
+ default: false,
155
+ hide: true
156
+ },
157
+ {
158
+ name: 'proxy',
159
+ help: 'Obsolete; use --corsProxy',
160
+ prompt: false
161
+ },
162
+ {
163
+ name: 'cors-proxy',
164
+ help: 'Serve the CORS proxy on this path',
165
+ when: function (answers) {
166
+ return answers['use-cors-proxy']
167
+ },
168
+ default: '/proxy',
169
+ prompt: true
170
+ },
171
+ {
172
+ name: 'auth-proxy',
173
+ help: 'Object with path/server pairs to reverse proxy',
174
+ default: {},
175
+ prompt: false,
176
+ hide: true
177
+ },
178
+ {
179
+ name: 'suppress-data-browser',
180
+ help: 'Suppress provision of a data browser',
181
+ flag: true,
182
+ prompt: false,
183
+ default: false,
184
+ hide: false
185
+ },
186
+ {
187
+ name: 'data-browser-path',
188
+ help: 'An HTML file which is sent to allow users to browse the data (eg using mashlib.js)',
189
+ question: 'Path of data viewer page (defaults to using mashlib)',
190
+ validate: validPath,
191
+ default: 'default',
192
+ prompt: false
193
+ },
194
+ {
195
+ name: 'suffix-acl',
196
+ full: 'suffix-acl',
197
+ help: "Suffix for acl files (default: '.acl')",
198
+ default: '.acl',
199
+ prompt: false
200
+ },
201
+ {
202
+ name: 'suffix-meta',
203
+ full: 'suffix-meta',
204
+ help: "Suffix for metadata files (default: '.meta')",
205
+ default: '.meta',
206
+ prompt: false
207
+ },
208
+ {
209
+ name: 'secret',
210
+ help: 'Secret used to sign the session ID cookie (e.g. "your secret phrase")',
211
+ question: 'Session secret for cookie',
212
+ default: 'random',
213
+ prompt: false,
214
+ filter: function (value) {
215
+ if (value === '' || value === 'random') {
216
+ return
217
+ }
218
+ return value
219
+ }
220
+ },
221
+ // {
222
+ // full: 'no-error-pages',
223
+ // flag: true,
224
+ // help: 'Disable custom error pages (use Node.js default pages instead)'
225
+ // },
226
+ {
227
+ name: 'error-pages',
228
+ help: 'Folder from which to look for custom error pages files (files must be named <error-code>.html -- eg. 500.html)',
229
+ validate: validPath,
230
+ prompt: false
231
+ },
232
+ {
233
+ name: 'force-user',
234
+ help: 'Force a WebID to always be logged in (useful when offline)'
235
+ },
236
+ {
237
+ name: 'strict-origin',
238
+ help: 'Enforce same origin policy in the ACL',
239
+ flag: true,
240
+ default: false,
241
+ prompt: false
242
+ },
243
+ {
244
+ name: 'use-email',
245
+ help: 'Do you want to set up an email service?',
246
+ flag: true,
247
+ prompt: true,
248
+ default: false
249
+ },
250
+ {
251
+ name: 'email-host',
252
+ help: 'Host of your email service',
253
+ prompt: true,
254
+ default: 'smtp.gmail.com',
255
+ when: (answers) => {
256
+ return answers['use-email']
257
+ }
258
+ },
259
+ {
260
+ name: 'email-port',
261
+ help: 'Port of your email service',
262
+ prompt: true,
263
+ default: '465',
264
+ when: (answers) => {
265
+ return answers['use-email']
266
+ }
267
+ },
268
+ {
269
+ name: 'email-auth-user',
270
+ help: 'User of your email service',
271
+ prompt: true,
272
+ when: (answers) => {
273
+ return answers['use-email']
274
+ },
275
+ validate: (value) => {
276
+ if (!value) {
277
+ return 'You must enter this information'
278
+ }
279
+ return true
280
+ }
281
+ },
282
+ {
283
+ name: 'email-auth-pass',
284
+ help: 'Password of your email service',
285
+ type: 'password',
286
+ prompt: true,
287
+ when: (answers) => {
288
+ return answers['use-email']
289
+ }
290
+ },
291
+ {
292
+ name: 'use-api-apps',
293
+ help: 'Do you want to load your default apps on /api/apps?',
294
+ flag: true,
295
+ prompt: false,
296
+ default: true
297
+ },
298
+ {
299
+ name: 'api-apps',
300
+ help: 'Path to the folder to mount on /api/apps',
301
+ prompt: true,
302
+ validate: validPath,
303
+ when: (answers) => {
304
+ return answers['use-api-apps']
305
+ }
306
+ },
307
+ { // copied from name: 'owner'
308
+ name: 'redirect-http-from',
309
+ help: 'HTTP port or \',\'-separated ports to redirect to the solid server port (e.g. "80,8080").',
310
+ prompt: false,
311
+ validate: function (value) {
312
+ if (!value.match(/^[0-9]+(,[0-9]+)*$/)) {
313
+ return 'direct-port(s) must be a comma-separated list of integers.'
314
+ }
315
+ const list = value.split(/,/).map(v => parseInt(v))
316
+ const bad = list.find(v => { return v < 1 || v > 65535 })
317
+ if (bad.length) {
318
+ return 'redirect-http-from port(s) ' + bad + ' out of range'
319
+ }
320
+ return true
321
+ }
322
+ },
323
+ {
324
+ // This property is packaged into an object for the server property in config.json
325
+ name: 'server-info-name', // All properties with prefix server-info- will be removed from the config
326
+ help: 'A name for your server (not required, but will be presented on your server\'s frontpage)',
327
+ prompt: true,
328
+ default: answers => new URL(answers['server-uri']).hostname
329
+ },
330
+ {
331
+ // This property is packaged into an object for the server property in config.json
332
+ name: 'server-info-description', // All properties with prefix server-info- will be removed from the config
333
+ help: 'A description of your server (not required)',
334
+ prompt: true
335
+ },
336
+ {
337
+ // This property is packaged into an object for the server property in config.json
338
+ name: 'server-info-logo', // All properties with prefix server-info- will be removed from the config
339
+ help: 'A logo that represents you, your brand, or your server (not required)',
340
+ prompt: true
341
+ },
342
+ {
343
+ name: 'enforce-toc',
344
+ help: 'Do you want to enforce Terms & Conditions for your service?',
345
+ flag: true,
346
+ prompt: true,
347
+ default: false,
348
+ when: answers => answers.multiuser
349
+ },
350
+ {
351
+ name: 'toc-uri',
352
+ help: 'URI to your Terms & Conditions',
353
+ prompt: true,
354
+ validate: validUri,
355
+ when: answers => answers['enforce-toc']
356
+ },
357
+ {
358
+ name: 'disable-password-checks',
359
+ help: 'Do you want to disable password strength checking?',
360
+ flag: true,
361
+ prompt: true,
362
+ default: false,
363
+ when: answers => answers.multiuser
364
+ },
365
+ {
366
+ name: 'support-email',
367
+ help: 'The support email you provide for your users (not required)',
368
+ prompt: true,
369
+ validate: (value) => {
370
+ if (value && !isEmail(value)) {
371
+ return 'Must be a valid email'
372
+ }
373
+ return true
374
+ },
375
+ when: answers => answers.multiuser
376
+ }
377
+ ]
378
+
379
+ function validPath (value) {
380
+ if (value === 'default') {
381
+ return Promise.resolve(true)
382
+ }
383
+ if (!value) {
384
+ return Promise.resolve('You must enter a valid path')
385
+ }
386
+ return new Promise((resolve) => {
387
+ fs.stat(value, function (err) {
388
+ if (err) return resolve('Nothing found at this path')
389
+ return resolve(true)
390
+ })
391
+ })
392
+ }
393
+
394
+ function validUri (value) {
395
+ if (!validUrl.isUri(value)) {
396
+ return 'Enter a valid uri (with protocol)'
397
+ }
398
+ return true
399
+ }