2019-08-03 00:34:00 +00:00
|
|
|
import cors from 'cors';
|
2019-07-10 00:21:25 +00:00
|
|
|
import express from 'express';
|
2021-10-05 04:54:12 +00:00
|
|
|
import ejs from 'ejs';
|
2022-05-12 01:28:46 +00:00
|
|
|
import os from "os";
|
2022-05-19 02:05:59 +00:00
|
|
|
import semver from 'semver';
|
2019-07-10 00:21:25 +00:00
|
|
|
|
2021-10-05 04:54:12 +00:00
|
|
|
import serveDirectoryMetadata, { getMetadataMoreEasier } from './actions/serveDirectoryMetadata.js';
|
2019-07-25 00:55:13 +00:00
|
|
|
import serveFileMetadata from './actions/serveFileMetadata.js';
|
2019-07-10 00:21:25 +00:00
|
|
|
import serveFile from './actions/serveFile.js';
|
2019-07-25 00:55:13 +00:00
|
|
|
import serveModule from './actions/serveModule.js';
|
2019-07-10 00:21:25 +00:00
|
|
|
|
2019-08-08 17:00:34 +00:00
|
|
|
import allowQuery from './middleware/allowQuery.js';
|
2019-07-25 00:55:13 +00:00
|
|
|
import findEntry from './middleware/findEntry.js';
|
2019-08-08 16:46:12 +00:00
|
|
|
import noQuery from './middleware/noQuery.js';
|
2019-07-10 00:21:25 +00:00
|
|
|
import redirectLegacyURLs from './middleware/redirectLegacyURLs.js';
|
2019-08-03 00:34:00 +00:00
|
|
|
import requestLog from './middleware/requestLog.js';
|
2019-07-25 00:55:13 +00:00
|
|
|
import validateFilename from './middleware/validateFilename.js';
|
2019-08-08 22:13:19 +00:00
|
|
|
import validatePackagePathname from './middleware/validatePackagePathname.js';
|
2019-07-10 00:21:25 +00:00
|
|
|
import validatePackageName from './middleware/validatePackageName.js';
|
2019-08-08 22:13:19 +00:00
|
|
|
import validatePackageVersion from './middleware/validatePackageVersion.js';
|
2019-07-10 00:21:25 +00:00
|
|
|
|
2022-05-19 02:05:59 +00:00
|
|
|
import { getVersionsAndTags } from './utils/npm.js';
|
|
|
|
|
|
|
|
|
2021-10-05 04:54:12 +00:00
|
|
|
const renderFolderBrowser = async (req, res, next) => {
|
|
|
|
|
2022-05-19 02:05:59 +00:00
|
|
|
function byVersion(a, b) {
|
|
|
|
return semver.lt(a, b) ? -1 : semver.gt(a, b) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getAvailableVersions(packageName, log) {
|
|
|
|
const versionsAndTags = await getVersionsAndTags(packageName, log);
|
|
|
|
return versionsAndTags ? versionsAndTags.versions.sort(byVersion) : [];
|
|
|
|
}
|
|
|
|
|
2021-10-05 04:54:12 +00:00
|
|
|
res.set({
|
|
|
|
'Cache-Control': 'public, max-age=14400', // 4 hours
|
|
|
|
'Cache-Tag': 'browse'
|
|
|
|
});
|
|
|
|
|
2022-05-19 02:05:59 +00:00
|
|
|
const availableVersions = await getAvailableVersions(
|
|
|
|
req.packageName,
|
|
|
|
req.log
|
|
|
|
);
|
|
|
|
|
2021-10-05 04:54:12 +00:00
|
|
|
const html = await ejs.render(`<%if(path!=="/"){path+="/"}%>
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8" />
|
|
|
|
<title>Index of <%= path %></title>
|
|
|
|
<style>
|
|
|
|
body {
|
|
|
|
font-size: 16px;
|
|
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
|
|
line-height: 1.5;
|
|
|
|
padding: 0 10px 5px
|
|
|
|
}
|
|
|
|
|
|
|
|
table {
|
|
|
|
width: 100%;
|
|
|
|
border-collapse: collapse;
|
|
|
|
font: .85em Monaco, monospace
|
|
|
|
}
|
|
|
|
|
|
|
|
tr.even {
|
|
|
|
background-color: #eee
|
|
|
|
}
|
|
|
|
|
|
|
|
th {
|
|
|
|
text-align: left
|
|
|
|
}
|
|
|
|
|
|
|
|
td,
|
|
|
|
th {
|
|
|
|
padding: .1em .25em
|
|
|
|
}
|
|
|
|
|
|
|
|
.version-wrapper {
|
|
|
|
line-height: 2.25em;
|
|
|
|
float: right
|
|
|
|
}
|
|
|
|
|
|
|
|
#version {
|
|
|
|
font-size: 1em
|
|
|
|
}
|
|
|
|
|
|
|
|
address {
|
|
|
|
text-align: right
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
|
|
|
|
<body>
|
2022-05-19 02:05:59 +00:00
|
|
|
<div class="version-wrapper"><select id="version"><% availableVersions.forEach(it=>{ %>
|
|
|
|
<option value="<%= it %>" <%= it === version ? "selected" : "" %> ><%= packageName+"@"+it %></option>
|
|
|
|
<% })%></select></div>
|
2021-10-05 04:54:12 +00:00
|
|
|
<h1>Index of <%= path %></h1>
|
|
|
|
<hr />
|
|
|
|
<table>
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th>Name</th>
|
|
|
|
<th>Type</th>
|
|
|
|
<th>Size</th>
|
|
|
|
<th>Last Modified</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
<%
|
|
|
|
files.sort((fileA,fileB) => {
|
|
|
|
let nameA = fileA.path.toUpperCase();
|
|
|
|
let nameB = fileB.path.toUpperCase();
|
|
|
|
if(fileA.type === "directory") return -1;
|
|
|
|
else if(fileB.type === "directory") return 1;
|
|
|
|
else if(nameA<nameB) return -1;
|
|
|
|
else if(nameA>nameB) return 1;
|
|
|
|
else return 0;
|
|
|
|
})
|
|
|
|
if(path!=="/"){
|
|
|
|
files.splice(0,0,{
|
|
|
|
path: "..",
|
|
|
|
type: "directory",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
%>
|
|
|
|
<% files.forEach((file,index) => { let name = file.path.replace(path,"")+(file.type==="directory"?"/":""); %>
|
|
|
|
<tr class="<%= index%2===0?"even":"odd" %> ">
|
|
|
|
<td><a title="<%= name %>" href="<%= name %>"><%= name %></a></td>
|
|
|
|
<td><%= file.type==="directory"?"-":file.contentType %></td>
|
|
|
|
<td><%= file.type==="directory"?"-":(()=>{
|
|
|
|
if(file.size<=1024) return file.size+" B";
|
|
|
|
else if(file.size<=1024*1024) return (file.size/1024).toFixed(2)+" KiB";
|
|
|
|
else return (file.size/1024/1024).toFixed(2)+" MB";
|
|
|
|
})() %></td>
|
|
|
|
<td><%= file.type==="directory"?"-":new Date(Date.parse(file.lastModified)).toISOString()
|
|
|
|
%></td>
|
|
|
|
</tr>
|
|
|
|
<% }) %>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
<hr />
|
2022-05-19 02:05:59 +00:00
|
|
|
<script>
|
|
|
|
var s = document.getElementById('version'), v = s.value
|
|
|
|
s.onchange = function () {
|
|
|
|
window.location.href = window.location.href.replace('@' + v, '@' + s.value)
|
|
|
|
}
|
|
|
|
</script>
|
2021-10-05 04:54:12 +00:00
|
|
|
<address><%= package %> </address>
|
2022-05-19 02:05:59 +00:00
|
|
|
<address>186526 UNPKG Network @sg-singapore-03 #<%= ((r=32)=>{
|
|
|
|
let s = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"
|
|
|
|
, n = s.length
|
|
|
|
, t = "";
|
|
|
|
for (let o = 0; o < r; o++)
|
|
|
|
t += s.charAt(Math.floor(Math.random() * n));
|
|
|
|
return t
|
|
|
|
})(8) %></address>
|
2021-10-05 04:54:12 +00:00
|
|
|
</body>
|
|
|
|
|
2022-05-19 02:05:59 +00:00
|
|
|
</html>`, Object.assign({ package: `${req.packageName}@${req.packageVersion}`, packageName: req.packageName, version: req.packageVersion, availableVersions: await getAvailableVersions(req.packageName, req.log) }, await getMetadataMoreEasier(req, res)), { async: true });
|
2021-10-05 04:54:12 +00:00
|
|
|
res.send(html);
|
|
|
|
};
|
|
|
|
|
2019-07-25 00:55:13 +00:00
|
|
|
function createApp(callback) {
|
2019-07-10 00:21:25 +00:00
|
|
|
const app = express();
|
2019-07-25 00:55:13 +00:00
|
|
|
callback(app);
|
|
|
|
return app;
|
|
|
|
}
|
2019-07-10 00:21:25 +00:00
|
|
|
|
2022-05-12 01:28:46 +00:00
|
|
|
const hostname = os.hostname();
|
|
|
|
|
2019-07-25 00:55:13 +00:00
|
|
|
export default function createServer() {
|
|
|
|
return createApp(app => {
|
|
|
|
app.disable('x-powered-by');
|
|
|
|
app.enable('trust proxy');
|
|
|
|
app.enable('strict routing');
|
2019-07-10 00:21:25 +00:00
|
|
|
|
2019-11-01 09:02:51 +00:00
|
|
|
app.use(cors());
|
2019-08-08 21:24:19 +00:00
|
|
|
app.use(express.static('public', { maxAge: '1y' }));
|
2019-07-10 00:21:25 +00:00
|
|
|
|
2022-05-12 01:28:46 +00:00
|
|
|
app.use((req, res, next) => {
|
|
|
|
res.set({
|
|
|
|
'x-delivery-by': hostname,
|
|
|
|
'timing-allow-origin': '*',
|
|
|
|
'x-powered-by': '186526 NPM Edge Network',
|
|
|
|
'x-version': 'd9e588d'
|
|
|
|
});
|
|
|
|
next();
|
|
|
|
})
|
|
|
|
|
2019-08-03 00:34:00 +00:00
|
|
|
app.use(requestLog);
|
|
|
|
|
2019-07-25 00:55:13 +00:00
|
|
|
app.use(redirectLegacyURLs);
|
2019-07-10 00:21:25 +00:00
|
|
|
|
2021-10-05 04:54:12 +00:00
|
|
|
app.all('/', (req, res) => {
|
2022-05-19 02:05:59 +00:00
|
|
|
res.setHeader('Content-Type',"text/plain; charset=utf-8")
|
2021-10-05 04:54:12 +00:00
|
|
|
res.send("To infinity and beyond!");
|
|
|
|
})
|
|
|
|
|
2022-05-12 01:28:46 +00:00
|
|
|
// app.use(
|
|
|
|
// '/browse',
|
|
|
|
// createApp(app => {
|
|
|
|
// app.enable('strict routing');
|
|
|
|
|
|
|
|
// app.get(
|
|
|
|
// '*/',
|
|
|
|
// noQuery(),
|
|
|
|
// validatePackagePathname,
|
|
|
|
// validatePackageName,
|
|
|
|
// validatePackageVersion,
|
|
|
|
// renderFolderBrowser,
|
|
|
|
// );
|
|
|
|
// app.get(
|
|
|
|
// '*',
|
|
|
|
// (req, res, next) => {
|
|
|
|
// res.redirect(req.path.replace("/browser", ""));
|
|
|
|
// }
|
|
|
|
// );
|
|
|
|
// })
|
|
|
|
// );
|
2019-07-25 00:55:13 +00:00
|
|
|
|
|
|
|
// We need to route in this weird way because Express
|
|
|
|
// doesn't have a way to route based on query params.
|
|
|
|
const metadataApp = createApp(app => {
|
|
|
|
app.enable('strict routing');
|
|
|
|
|
|
|
|
app.get(
|
|
|
|
'*/',
|
2019-08-08 17:00:34 +00:00
|
|
|
allowQuery('meta'),
|
2019-08-08 22:13:19 +00:00
|
|
|
validatePackagePathname,
|
2019-07-25 00:55:13 +00:00
|
|
|
validatePackageName,
|
2019-08-08 22:13:19 +00:00
|
|
|
validatePackageVersion,
|
2019-07-25 00:55:13 +00:00
|
|
|
validateFilename,
|
|
|
|
serveDirectoryMetadata
|
|
|
|
);
|
|
|
|
|
|
|
|
app.get(
|
|
|
|
'*',
|
2019-08-08 17:00:34 +00:00
|
|
|
allowQuery('meta'),
|
2019-08-08 22:13:19 +00:00
|
|
|
validatePackagePathname,
|
2019-07-25 00:55:13 +00:00
|
|
|
validatePackageName,
|
2019-08-08 22:13:19 +00:00
|
|
|
validatePackageVersion,
|
2019-07-25 00:55:13 +00:00
|
|
|
validateFilename,
|
|
|
|
serveFileMetadata
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.use((req, res, next) => {
|
|
|
|
if (req.query.meta != null) {
|
|
|
|
metadataApp(req, res);
|
|
|
|
} else {
|
|
|
|
next();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-08-08 17:00:34 +00:00
|
|
|
// We need to route in this weird way because Express
|
|
|
|
// doesn't have a way to route based on query params.
|
2019-07-25 00:55:13 +00:00
|
|
|
const moduleApp = createApp(app => {
|
|
|
|
app.enable('strict routing');
|
|
|
|
|
|
|
|
app.get(
|
|
|
|
'*',
|
2019-08-08 17:00:34 +00:00
|
|
|
allowQuery('module'),
|
2019-08-08 22:13:19 +00:00
|
|
|
validatePackagePathname,
|
2019-07-25 00:55:13 +00:00
|
|
|
validatePackageName,
|
2019-08-08 22:13:19 +00:00
|
|
|
validatePackageVersion,
|
2019-07-25 00:55:13 +00:00
|
|
|
validateFilename,
|
|
|
|
findEntry,
|
|
|
|
serveModule
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.use((req, res, next) => {
|
|
|
|
if (req.query.module != null) {
|
|
|
|
moduleApp(req, res);
|
|
|
|
} else {
|
|
|
|
next();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-10-05 04:54:12 +00:00
|
|
|
app.get('*/', noQuery(),
|
|
|
|
validatePackagePathname,
|
|
|
|
validatePackageName,
|
|
|
|
validatePackageVersion,
|
|
|
|
renderFolderBrowser
|
|
|
|
);
|
2019-07-25 00:55:13 +00:00
|
|
|
|
|
|
|
app.get(
|
|
|
|
'*',
|
2019-08-08 17:00:34 +00:00
|
|
|
noQuery(),
|
2019-08-08 22:13:19 +00:00
|
|
|
validatePackagePathname,
|
2019-07-25 00:55:13 +00:00
|
|
|
validatePackageName,
|
2019-08-08 22:13:19 +00:00
|
|
|
validatePackageVersion,
|
2019-07-25 00:55:13 +00:00
|
|
|
validateFilename,
|
|
|
|
findEntry,
|
|
|
|
serveFile
|
|
|
|
);
|
|
|
|
});
|
2019-07-10 00:21:25 +00:00
|
|
|
}
|