UNPKG

decentralized-internet

Version:

An NPM library of programs to create decentralized web and distributed computing projects

202 lines (163 loc) 5.71 kB
### * Federated Wiki : Node Server * * Copyright Ward Cunningham and other contributors * Licensed under the MIT license. * https://github.com/fedwiki/wiki/blob/master/LICENSE.txt ### # **farm.coffee** # The farm module works by putting a bouncy host based proxy # in front of servers that it creates path = require 'path' fs = require 'fs' chokidar = require 'chokidar' http = require 'http' socketio = require 'socket.io' server = require 'wiki-server' _ = require('lodash') errorPage = require './error-page' module.exports = exports = (argv) -> # Map incoming hosts to their wiki's port hosts = {} # Keep an array of servers that are currently active runningServers = [] if argv.allowed if argv.allowed is '*' # we will allow any wiki which we have a directory exists wikiDir = argv.data allowedHosts = fs.readdirSync(wikiDir, {withFileTypes: true}) .filter (item) -> item.isDirectory() .map (item) -> item.name # watch for new wiki directories being created (or deleted), and keep allowedHosts up to date watcher = chokidar.watch wikiDir, { persistent: true ignoreInitial: true depth: 0 } watcher .on 'addDir', (newWiki) -> newWiki = path.basename(newWiki) allowedHosts.push(newWiki) .on 'unlinkDir', (delWiki) -> delWiki = path.basename(delWiki) _.pull(allowedHosts, delWiki) else # we have a list of wiki that are allowed allowedHosts = argv.allowed .split ',' # split up the coma seperated list of allowed hosts .map (item) -> item.trim() # trim any whitespace padding from the items in the list allowHost = (host) -> hostDomain = _.split(host, ':')[0] if _.includes(allowedHosts, hostDomain) return true else return false if argv.wikiDomains wikiDomains = _.keys(argv.wikiDomains) inWikiDomain = '' allowDomain = (host) -> hostDomain = _.split(host, ':')[0] inWikiDomain = '' _.each wikiDomains, (domain) -> if _.endsWith hostDomain, domain inWikiDomain = domain if inWikiDomain return true else return false else allowDomain = () -> true allow = (host) -> # wikiDomains and allowed should both be optional if argv.allowed if allowHost(host) # host is in the allowed list if argv.wikiDomains if allowDomain(host) # host is within a defined wikiDomain return true else # while host is in the allowed list, it is not within an allowed domain return false return true else # host is not within the allowed list return false if argv.wikiDomains if allowDomain(host) # host is within a defined wikiDomain return true else # host is not within a defined wikiDomain return false # neither wikiDomain or allowed are configured return true farmServ = http.createServer (req, res) -> if req.headers?.host incHost = req.headers.host.split(':')[0] else res.statusCode = 400 res.end('Missing host header') return # If the host starts with "www." treat it the same as if it didn't if incHost[0..3] is "www." incHost = incHost[4..] # if we already have a port for this host, forward the request to it. if hosts[incHost] hosts[incHost](req, res) else # check that request is for an allowed host unless allow(incHost) res.statusCode = 400 errorText = errorPage.render('Requested Wiki Does Not Exist','The wiki you are trying to access does not exist.','') res.end(errorText) return # Create a new options object, copy over the options used to start the # farm, and modify them to make sense for servers spawned from the farm. # do deep copy, needed for nested configurations - e.g. config of wiki domains clone = (map) -> copy = undefined if map is null or typeof map isnt 'object' return map if map instanceof Array copy = [] i = 0 len = map.length while i < len copy[i] = clone(map[i]) i++ return copy if typeof map is 'object' copy = {} for attr of map copy[attr] = clone(map[attr]) return copy console.log "Unsupported:", typeof map return newargv = clone argv newargv.data = if argv.data path.join(argv.data, incHost.split(':')[0]) else path.join(argv.root, 'data', incHost.split(':')[0]) newargv.url = "http://#{incHost}" # apply wiki domain configuration, if defined if inWikiDomain newargv = _.assignIn newargv, newargv.wikiDomains[inWikiDomain] newargv.wiki_domain = inWikiDomain # Create a new server, add it to the list of servers, and # once it's ready send the request to it. local = server(newargv) local.io = io hosts[incHost] = local runningServers.push(local) # patch in new neighbors if argv.autoseed neighbors = if argv.neighbors then argv.neighbors + ',' else '' neighbors += Object.keys(hosts).join(',') runningServers.forEach (server) -> server.startOpts.neighbors = neighbors local.once "owner-set", -> local.emit 'running-serv', farmServ hosts[incHost](req, res) io = socketio(farmServ) runningFarmServ = farmServ.listen(argv.port, argv.host)