secure-scuttlebot classic
1#! /usr/bin/env node
2
3process.env.CHLORIDE_JS = process.env.CHLORIDE_JS || '1'
4
5var fs = require('fs')
6var path = require('path')
7var pull = require('pull-stream')
8var toPull = require('stream-to-pull-stream')
9var File = require('pull-file')
10var spawn = require('cross-spawn')
11var explain = require('explain-error')
12var minimist = require('minimist')
13var muxrpcli = require('muxrpcli')
14var cmdAliases = require('./lib/cli-cmd-aliases')
15var ProgressBar = require('./lib/progress')
16var cliHelp = require('./lib/cli-help')
17var homeDir = require('os-homedir')
18var packageJson = require('./package.json')
19
20//get config as cli options after --, options before that are
21//options to the command.
22var argv = process.argv.slice(2)
23var i = argv.indexOf('--')
24var conf = i === -1 ? [] : argv.slice(i+1)
25argv = i === -1 ? argv : argv.slice(0, i)
26
27var overrides = minimist(conf)
28var envAppName = process.env.ssb_appname
29var appName = envAppName || overrides.appname || overrides.ssb_appname || 'ssb'
30var baseHome = homeDir() || 'browser'
31var basePath = overrides.path || path.join(baseHome, '.' + appName)
32var manifestForHelp = {}
33var manifestPathForHelp = path.join(basePath, 'manifest.json')
34if (fs.existsSync(manifestPathForHelp)) {
35 try {
36 manifestForHelp = JSON.parse(fs.readFileSync(manifestPathForHelp))
37 } catch (err) {
38 manifestForHelp = {}
39 }
40}
41var helpCatalog = cliHelp.createCatalog(manifestForHelp)
42
43var helpFlags = ['--help', '-h']
44
45function printCliHelp () {
46 var name = packageJson.name || 'ssb-server'
47 console.log(name + ' ' + packageJson.version)
48 console.log('Usage:')
49 console.log(' ' + name + ' start [--verbose]')
50 console.log(' ' + name + ' <rpc.command> [arguments]')
51 console.log(' ' + name + ' help [command]')
52 console.log('')
53 console.log('Options:')
54 console.log(' --help, -h show this message')
55 console.log(' --verbose show verbose RPC errors')
56 console.log(' -- <key>=<value> pass overrides to ssb-config')
57 console.log('')
58 console.log('Top-level commands (request detailed help for any of them):')
59 var topLevelCommands = helpCatalog.names.filter(function (name) {
60 return name.indexOf('.') === -1
61 })
62 var preview = topLevelCommands.slice(0, 12)
63 console.log(' ' + preview.join(', '))
64 if (topLevelCommands.length > preview.length) {
65 console.log(' ...and ' + (topLevelCommands.length - preview.length) + ' more. Use `' + name + ' help <command>` to see specifics.')
66 } else {
67 console.log(' (Call `' + name + ' help <command>` for the detailed universe of commands.)')
68 }
69 console.log(' Call `' + name + ' list-commands` to dump every command name.')
70 console.log('')
71 console.log('Examples:')
72 console.log(' ' + name + ' start')
73 console.log(' ' + name + ' friends.hops alice')
74 console.log(' ' + name + ' start -- --port 8008')
75 console.log('')
76 console.log('Run `' + name + ' help <command>` for more detail on a specific command.')
77}
78
79function printCommandHelp (requestedCommand) {
80 var resolved = requestedCommand
81 var entry = helpCatalog.get(resolved)
82 if (!entry && cmdAliases[resolved]) {
83 resolved = cmdAliases[resolved]
84 entry = helpCatalog.get(resolved)
85 }
86
87 if (!entry) {
88 console.log('No help data is currently available for `' + requestedCommand + '`.')
89 console.log('Start the server to generate ' + manifestPathForHelp + ' and re-run `' + packageJson.name + ' help ' + requestedCommand + '`.')
90 return
91 }
92
93 console.log('Help for `' + resolved + '`' + (resolved !== requestedCommand ? ' (matched from alias ' + requestedCommand + ')' : '') + ':')
94 if (entry.description)
95 console.log(' ' + entry.description)
96 if (entry.type)
97 console.log('Type: ' + entry.type)
98 var argNames = Object.keys(entry.args || {})
99 if (argNames.length) {
100 console.log('Arguments:')
101 argNames.forEach(function (argName) {
102 var arg = entry.args[argName] || {}
103 var type = arg.type || 'value'
104 var desc = arg.description || ''
105 console.log(' --' + argName + ' <' + type + '>' + (desc ? ' - ' + desc : ''))
106 })
107 }
108 console.log('Example: ' + entry.example)
109}
110
111function printCommandList () {
112 console.log('All available commands:')
113 helpCatalog.names.forEach(function (name) {
114 console.log(' ' + name)
115 })
116}
117
118if (argv[0] === 'help') {
119 if (argv[1])
120 printCommandHelp(argv[1])
121 else
122 printCliHelp()
123 process.exit(0)
124}
125
126if (argv[0] === 'list-commands') {
127 printCommandList()
128 process.exit(0)
129}
130
131if (argv.length === 0 || helpFlags.indexOf(argv[0]) !== -1) {
132 printCliHelp()
133 process.exit(0)
134}
135
136var Config = require('ssb-config/inject')
137var config = Config(process.env.ssb_appname, overrides)
138
139if (config.ws !== false) {
140 if (!config.ws || typeof config.ws !== 'object') config.ws = {}
141 if (typeof config.ws.port !== 'number') config.ws.port = 8989
142 if (typeof config.ws.host !== 'string') config.ws.host = '127.0.0.1'
143}
144
145if (config.keys.curve === 'k256')
146 throw new Error('k256 curves are no longer supported,'+
147 'please delete' + path.join(config.path, 'secret'))
148
149var manifestFile = path.join(config.path, 'manifest.json')
150
151if (argv[0] == 'server') {
152 console.log('WARNING-DEPRECATION: `sbot server` has been renamed to `ssb-server start`')
153 argv[0] = 'start'
154}
155
156if (argv[0] == 'start') {
157 console.log(packageJson.name, packageJson.version, config.path, 'logging.level:'+config.logging.level)
158 console.log('my key ID:', config.keys.public)
159
160 // special start command:
161 // import ssbServer and start the server
162
163 var createSsbServer = require('./')
164 .use(require('ssb-private1'))
165 .use(require('ssb-onion'))
166 .use(require('ssb-unix-socket'))
167 .use(require('ssb-no-auth'))
168 .use(require('ssb-plugins'))
169 .use(require('ssb-master'))
170 .use(require('ssb-gossip'))
171 .use(require('ssb-replicate'))
172 .use(require('./plugins/friends'))
173 .use(require('ssb-blobs'))
174 .use(require('./plugins/invite'))
175 .use(require('./plugins/decent-ui'))
176 .use(require('ssb-local'))
177 .use(require('ssb-logging'))
178 .use(require('ssb-query'))
179 .use(require('ssb-links'))
180 .use(require('ssb-ws'))
181 .use(require('./lib/frontend'))
182 .use(require('ssb-ebt'))
183 .use(require('ssb-ooo'))
184 // add third-party plugins
185
186 require('ssb-plugins').loadUserPlugins(createSsbServer, config)
187
188 // start server
189 var server = createSsbServer(config)
190
191 // write RPC manifest to ~/.ssb/manifest.json
192 fs.writeFileSync(manifestFile, JSON.stringify(server.getManifest(), null, 2))
193
194 if(process.stdout.isTTY && (config.logging.level != 'info'))
195 ProgressBar(server.progress)
196} else {
197 // normal command:
198 // create a client connection to the server
199
200 // read manifest.json
201 var manifest
202 try {
203 manifest = JSON.parse(fs.readFileSync(manifestFile))
204 } catch (err) {
205 throw explain(err,
206 'no manifest file'
207 + '- should be generated first time server is run'
208 )
209 }
210
211 var opts = {
212 manifest: manifest,
213 port: config.port,
214 host: config.host || 'localhost',
215 caps: config.caps,
216 key: config.key || config.keys.id
217 }
218
219 var Client = require('ssb-client')
220
221 // connect
222 Client(config.keys, opts, function (err, rpc) {
223 if(err) {
224 if (/could not connect/.test(err.message)) {
225 console.error('Error: Could not connect to ssb-server ' + opts.host + ':' + opts.port)
226 console.error('Use the "start" command to start it.')
227 console.error('Use --verbose option to see full error')
228 if(config.verbose) throw err
229 process.exit(1)
230 }
231 throw err
232 }
233
234 // add aliases
235 for (var k in cmdAliases) {
236 rpc[k] = rpc[cmdAliases[k]]
237 manifest[k] = manifest[cmdAliases[k]]
238 }
239
240 // add some extra commands
241// manifest.version = 'async'
242 manifest.config = 'sync'
243// rpc.version = function (cb) {
244// console.log(packageJson.version)
245// cb()
246// }
247 rpc.config = function (cb) {
248 console.log(JSON.stringify(config, null, 2))
249 cb()
250 }
251
252 if (process.argv[2] === 'blobs.add') {
253 var filename = process.argv[3]
254 var source =
255 filename ? File(process.argv[3])
256 : !process.stdin.isTTY ? toPull.source(process.stdin)
257 : (function () {
258 console.error('USAGE:')
259 console.error(' blobs.add <filename> # add a file')
260 console.error(' source | blobs.add # read from stdin')
261 process.exit(1)
262 })()
263 pull(
264 source,
265 rpc.blobs.add(function (err, hash) {
266 if (err)
267 throw err
268 console.log(hash)
269 process.exit()
270 })
271 )
272 return
273 }
274
275 if (process.argv[2] === 'git-ssb') {
276 var gitArgs = process.argv.slice(3)
277 var gitPath
278 try {
279 gitPath = require.resolve('git-ssb/bin/git-ssb')
280 } catch (e) {
281 console.error('Error: vendored git-ssb not found in this ssb-server install.')
282 console.error('Try running: npm install git-ssb --save')
283 process.exit(1)
284 }
285
286 var child = spawn(process.execPath, [gitPath].concat(gitArgs), {stdio: 'inherit'})
287
288 child.on('exit', function (code, signal) {
289 if (typeof code === 'number')
290 process.exit(code)
291 else if (signal)
292 process.exit(1)
293 else
294 process.exit(0)
295 })
296 return
297 }
298
299 // run commandline flow
300 muxrpcli(argv, manifest, rpc, config.verbose)
301 })
302}