1 |
|
2 | async = require 'async'
|
3 | pkgcloud = require 'pkgcloud'
|
4 | fs = require 'fs'
|
5 | pathutil = require 'path'
|
6 | {BufferStream, extend} = require('./util')
|
7 | ClientRack = require('./.').ClientRack
|
8 | {EventEmitter} = require 'events'
|
9 |
|
10 | class exports.Rack extends EventEmitter
|
11 | constructor: (assets, options) ->
|
12 | super()
|
13 | options ?= {}
|
14 | @maxAge = options.maxAge
|
15 | @allowNoHashCache = options.allowNoHashCache
|
16 | @on 'complete', =>
|
17 | @completed = true
|
18 | @on 'newListener', (event, listener) =>
|
19 | if event is 'complete' and @completed is true
|
20 | listener()
|
21 | @on 'error', (error) =>
|
22 | throw error if @listeners('error').length is 1
|
23 | for asset in assets
|
24 | asset.rack = this
|
25 | @assets = []
|
26 | async.forEachSeries assets, (asset, next) =>
|
27 | asset.on 'error', (error) =>
|
28 | next error
|
29 | asset.on 'complete', =>
|
30 | if asset.contents?
|
31 | @assets.push asset
|
32 | if asset.assets?
|
33 | @assets = @assets.concat asset.assets
|
34 | next()
|
35 | asset.rack = this
|
36 | asset.emit 'start'
|
37 | , (error) =>
|
38 | return @emit 'error', error if error?
|
39 | @emit 'complete'
|
40 |
|
41 | createClientRack: ->
|
42 | clientRack = new ClientRack
|
43 | clientRack.rack = this
|
44 | clientRack.emit 'start'
|
45 | clientRack
|
46 |
|
47 | addClientRack: (next) ->
|
48 | clientRack = @createClientRack()
|
49 | clientRack.on 'complete', =>
|
50 | @assets.push clientRack
|
51 | next()
|
52 |
|
53 | handle: (request, response, next) ->
|
54 | response.locals assets: this
|
55 | handle = =>
|
56 | for asset in @assets
|
57 | check = asset.checkUrl request.url
|
58 | return asset.respond request, response if check
|
59 | next()
|
60 | if @completed
|
61 | handle()
|
62 | else @on 'complete', handle
|
63 |
|
64 | writeConfigFile: (filename) ->
|
65 | config = {}
|
66 | for asset in @assets
|
67 | config[asset.url] = asset.specificUrl
|
68 | fs.writeFileSync filename, JSON.stringify(config)
|
69 |
|
70 | deploy: (options, next) ->
|
71 | options.keyId = options.accessKey
|
72 | options.key = options.secretKey
|
73 | deploy = =>
|
74 | client = pkgcloud.storage.createClient options
|
75 | assets = @assets
|
76 |
|
77 |
|
78 |
|
79 | assets = @assets.concat @assets[0] if options.provider is 'rackspace'
|
80 | async.forEachSeries assets, (asset, next) =>
|
81 | console.log asset.url
|
82 | stream = null
|
83 | if asset.gzip
|
84 | stream = new BufferStream asset.gzipContents
|
85 | else
|
86 | stream = new BufferStream asset.contents
|
87 | url = asset.specificUrl.slice 1, asset.specificUrl.length
|
88 | headers = {}
|
89 | for key, value of asset.headers
|
90 | headers[key] = value
|
91 | headers['x-amz-acl'] = 'public-read' if options.provider is 'amazon'
|
92 | clientOptions =
|
93 | container: options.container
|
94 | remote: url
|
95 | headers: headers
|
96 | stream: stream
|
97 | client.upload clientOptions, (error) ->
|
98 | return next error if error?
|
99 | next()
|
100 | , (error) =>
|
101 | if error?
|
102 | return next error if next?
|
103 | throw error
|
104 | if options.configFile?
|
105 | @writeConfigFile options.configFile
|
106 | next() if next?
|
107 | if @completed
|
108 | deploy()
|
109 | else @on 'complete', deploy
|
110 |
|
111 | tag: (url) ->
|
112 | for asset in @assets
|
113 | return asset.tag() if asset.url is url
|
114 | throw new Error "No asset found for url: #{url}"
|
115 |
|
116 | url: (url) ->
|
117 | for asset in @assets
|
118 | return asset.specificUrl if url is asset.url
|
119 |
|
120 | @extend: extend
|
121 |
|
122 | class ConfigRack
|
123 | constructor: (options) ->
|
124 | throw new Error('options.configFile is required') unless options.configFile?
|
125 | throw new Error('options.hostname is required') unless options.hostname?
|
126 | @assetMap = require options.configFile
|
127 | @hostname = options.hostname
|
128 |
|
129 | handle: (request, response, next) ->
|
130 | response.locals assets: this
|
131 | for url, specificUrl of @assetMap
|
132 | if request.url is url or request.url is specificUrl
|
133 | return response.redirect "//#{@hostname}#{specificUrl}"
|
134 | next()
|
135 | tag: (url) ->
|
136 | switch pathutil.extname(url)
|
137 | when '.js'
|
138 | tag = "\n<script type=\"text/javascript\" "
|
139 | return tag += "src=\"//#{@hostname}#{@assetMap[url]}\"></script>"
|
140 | when '.css'
|
141 | return "\n<link rel=\"stylesheet\" href=\"//#{@hostname}#{@assetMap[url]}\">"
|
142 | url: (url) ->
|
143 | return "//#{@hostname}#{@assetMap[url]}"
|
144 |
|
145 |
|
146 | exports.fromConfigFile = (options) ->
|
147 | return new ConfigRack(options)
|