Add /_meta endpoint for metadata
Also, add integrity values to metadata.
This commit is contained in:
parent
bc609ca825
commit
666d8afc95
|
@ -32,6 +32,7 @@
|
||||||
"react-router-dom": "^4.0.0",
|
"react-router-dom": "^4.0.0",
|
||||||
"redis": "^2.7.1",
|
"redis": "^2.7.1",
|
||||||
"semver": "^5.3.0",
|
"semver": "^5.3.0",
|
||||||
|
"sri-toolbox": "^0.2.0",
|
||||||
"tar-fs": "^1.15.2",
|
"tar-fs": "^1.15.2",
|
||||||
"throng": "^4.0.0",
|
"throng": "^4.0.0",
|
||||||
"validate-npm-package-name": "^3.0.0"
|
"validate-npm-package-name": "^3.0.0"
|
||||||
|
|
|
@ -9,6 +9,7 @@ const { fetchStats } = require('./cloudflare')
|
||||||
const parsePackageURL = require('./middleware/parsePackageURL')
|
const parsePackageURL = require('./middleware/parsePackageURL')
|
||||||
const fetchFile = require('./middleware/fetchFile')
|
const fetchFile = require('./middleware/fetchFile')
|
||||||
const serveFile = require('./middleware/serveFile')
|
const serveFile = require('./middleware/serveFile')
|
||||||
|
const serveMetadata = require('./middleware/serveMetadata')
|
||||||
|
|
||||||
morgan.token('fwd', function (req) {
|
morgan.token('fwd', function (req) {
|
||||||
return req.get('x-forwarded-for').replace(/\s/g, '')
|
return req.get('x-forwarded-for').replace(/\s/g, '')
|
||||||
|
@ -66,9 +67,8 @@ function createServer() {
|
||||||
maxAge: '365d'
|
maxAge: '365d'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
app.use(parsePackageURL)
|
app.use('/_meta', parsePackageURL, fetchFile, serveMetadata)
|
||||||
app.use(fetchFile)
|
app.use('/', parsePackageURL, fetchFile, serveFile)
|
||||||
app.use(serveFile)
|
|
||||||
|
|
||||||
const server = http.createServer(app)
|
const server = http.createServer(app)
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const SRIToolbox = require('sri-toolbox')
|
||||||
const { getContentType, getStats, getFileType } = require('./FileUtils')
|
const { getContentType, getStats, getFileType } = require('./FileUtils')
|
||||||
|
|
||||||
function getEntries(dir, file, maximumDepth) {
|
function getEntries(dir, file, maximumDepth) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise(function (resolve, reject) {
|
||||||
fs.readdir(path.join(dir, file), (error, files) => {
|
fs.readdir(path.join(dir, file), function (error, files) {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error)
|
reject(error)
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,7 +16,7 @@ function getEntries(dir, file, maximumDepth) {
|
||||||
})
|
})
|
||||||
).then(function (statsArray) {
|
).then(function (statsArray) {
|
||||||
return Promise.all(statsArray.map(function (stats, index) {
|
return Promise.all(statsArray.map(function (stats, index) {
|
||||||
return getMetadata(dir, path.join(file, files[index]), stats, maximumDepth - 1)
|
return getMetadataRecursive(dir, path.join(file, files[index]), stats, maximumDepth - 1)
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -28,7 +29,19 @@ function formatTime(time) {
|
||||||
return new Date(time).toISOString()
|
return new Date(time).toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMetadata(dir, file, stats, maximumDepth) {
|
function getIntegrity(file) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
fs.readFile(file, function (error, data) {
|
||||||
|
if (error) {
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
resolve(SRIToolbox.generate({ algorithms: [ 'sha384' ] }, data))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMetadataRecursive(dir, file, stats, maximumDepth) {
|
||||||
const metadata = {
|
const metadata = {
|
||||||
lastModified: formatTime(stats.mtime),
|
lastModified: formatTime(stats.mtime),
|
||||||
contentType: getContentType(file),
|
contentType: getContentType(file),
|
||||||
|
@ -37,6 +50,13 @@ function getMetadata(dir, file, stats, maximumDepth) {
|
||||||
type: getFileType(stats)
|
type: getFileType(stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stats.isFile()) {
|
||||||
|
return getIntegrity(path.join(dir, file)).then(function (integrity) {
|
||||||
|
metadata.integrity = integrity
|
||||||
|
return metadata
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!stats.isDirectory() || maximumDepth === 0)
|
if (!stats.isDirectory() || maximumDepth === 0)
|
||||||
return Promise.resolve(metadata)
|
return Promise.resolve(metadata)
|
||||||
|
|
||||||
|
@ -46,12 +66,12 @@ function getMetadata(dir, file, stats, maximumDepth) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateMetadata(baseDir, path, stats, maximumDepth, callback) {
|
function getMetadata(baseDir, path, stats, maximumDepth, callback) {
|
||||||
return getMetadata(baseDir, path, stats, maximumDepth).then(function (metadata) {
|
getMetadataRecursive(baseDir, path, stats, maximumDepth).then(function (metadata) {
|
||||||
callback(null, metadata)
|
callback(null, metadata)
|
||||||
}, callback)
|
}, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
get: generateMetadata
|
get: getMetadata
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const qs = require('querystring')
|
||||||
const etag = require('etag')
|
const etag = require('etag')
|
||||||
const Metadata = require('./MetadataUtils')
|
|
||||||
const { generateDirectoryIndexHTML } = require('./IndexUtils')
|
const { generateDirectoryIndexHTML } = require('./IndexUtils')
|
||||||
const { getContentType } = require('./FileUtils')
|
const { getContentType } = require('./FileUtils')
|
||||||
|
|
||||||
|
@ -10,11 +10,6 @@ const { getContentType } = require('./FileUtils')
|
||||||
*/
|
*/
|
||||||
const AutoIndex = !process.env.DISABLE_INDEX
|
const AutoIndex = !process.env.DISABLE_INDEX
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum recursion depth for ?meta listings.
|
|
||||||
*/
|
|
||||||
const MaximumDepth = 128
|
|
||||||
|
|
||||||
function sendFile(res, file, stats) {
|
function sendFile(res, file, stats) {
|
||||||
let contentType = getContentType(file)
|
let contentType = getContentType(file)
|
||||||
|
|
||||||
|
@ -41,20 +36,12 @@ function sendFile(res, file, stats) {
|
||||||
* Send the file, JSON metadata, or HTML directory listing.
|
* Send the file, JSON metadata, or HTML directory listing.
|
||||||
*/
|
*/
|
||||||
function serveFile(req, res, next) {
|
function serveFile(req, res, next) {
|
||||||
// TODO: remove support for "json" query param
|
|
||||||
if (req.query.meta != null || req.query.json != null) {
|
if (req.query.meta != null || req.query.json != null) {
|
||||||
Metadata.get(req.packageDir, req.file, req.stats, MaximumDepth, function (error, metadata) {
|
// Preserve support for ?meta and ?json for backwards compat.
|
||||||
if (error) {
|
delete req.query.meta
|
||||||
console.error(error)
|
delete req.query.json
|
||||||
res.status(500).type('text').send(`Cannot generate JSON metadata for ${req.packageSpec}${req.filename}`)
|
const search = qs.stringify(req.query)
|
||||||
} else {
|
res.status(301).redirect(`/_meta${req.pathname}${search}`)
|
||||||
// Cache metadata for 1 year.
|
|
||||||
res.set({
|
|
||||||
'Cache-Control': 'public, max-age=31536000',
|
|
||||||
'Cache-Tag': 'meta'
|
|
||||||
}).send(metadata)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (req.stats.isFile()) {
|
} else if (req.stats.isFile()) {
|
||||||
// Cache files for 1 year.
|
// Cache files for 1 year.
|
||||||
res.set({
|
res.set({
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
const Metadata = require('./MetadataUtils')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum recursion depth for ?meta listings.
|
||||||
|
*/
|
||||||
|
const MaximumDepth = 128
|
||||||
|
|
||||||
|
function serveMetadata(req, res) {
|
||||||
|
Metadata.get(req.packageDir, req.file, req.stats, MaximumDepth, function (error, metadata) {
|
||||||
|
if (error) {
|
||||||
|
console.error(error)
|
||||||
|
res.status(500).type('text').send(`Cannot generate metadata for ${req.packageSpec}${req.filename}`)
|
||||||
|
} else {
|
||||||
|
// Cache metadata for 1 year.
|
||||||
|
res.set({
|
||||||
|
'Cache-Control': 'public, max-age=31536000',
|
||||||
|
'Cache-Tag': 'meta'
|
||||||
|
}).send(metadata)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = serveMetadata
|
|
@ -4929,6 +4929,10 @@ sprintf-js@~1.0.2:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
|
|
||||||
|
sri-toolbox@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sri-toolbox/-/sri-toolbox-0.2.0.tgz#a7fea5c3fde55e675cf1c8c06f3ebb5c2935835e"
|
||||||
|
|
||||||
sshpk@^1.7.0:
|
sshpk@^1.7.0:
|
||||||
version "1.13.0"
|
version "1.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c"
|
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c"
|
||||||
|
|
Loading…
Reference in New Issue