Experimental port to Firebase hosting
This commit is contained in:
parent
e4d6df255e
commit
31e7d3865a
13
.eslintrc
13
.eslintrc
|
@ -5,7 +5,8 @@
|
||||||
},
|
},
|
||||||
"extends": ["eslint:recommended", "plugin:import/errors"],
|
"extends": ["eslint:recommended", "plugin:import/errors"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": 0
|
"no-console": 0,
|
||||||
|
"import/no-unresolved": 0
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"fetch": true,
|
"fetch": true,
|
||||||
|
@ -13,7 +14,15 @@
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"react": {
|
"react": {
|
||||||
"version": "15"
|
"version": "16"
|
||||||
|
},
|
||||||
|
"import/resolver": {
|
||||||
|
"node": {
|
||||||
|
"moduleDirectory": [
|
||||||
|
"node_modules",
|
||||||
|
"functions/node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"projects": {
|
||||||
|
"default": "unpkg-gcp"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
firebase-debug.log*
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
/.env
|
/.env
|
||||||
|
/functions/*.js
|
||||||
|
/functions/node_modules/
|
||||||
/node_modules/
|
/node_modules/
|
||||||
/public/_assets/
|
/public/_assets/
|
||||||
/dump.rdb
|
/manifest.json
|
||||||
/stats.json
|
|
||||||
/secret_key
|
/secret_key
|
||||||
/tokens/
|
/tokens/
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"public/_assets/main.js": {
|
||||||
|
"bundled": 106742,
|
||||||
|
"minified": 41138,
|
||||||
|
"gzipped": 12988
|
||||||
|
},
|
||||||
|
"public/_assets/autoIndex.js": {
|
||||||
|
"bundled": 44018,
|
||||||
|
"minified": 15077,
|
||||||
|
"gzipped": 5142
|
||||||
|
}
|
||||||
|
}
|
21
.travis.yml
21
.travis.yml
|
@ -1,17 +1,20 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: node
|
node_js: node
|
||||||
cache: npm
|
cache: npm
|
||||||
services:
|
env:
|
||||||
- redis-server
|
secure: Z/uibhTgn2S4fy12WQs1wopytecb0Eo2O1qkT4FyEuu4xjSM0ZQ5zKP3eRSRAfr7tTZbTro1zRGWjNCQjJSNvm/6Ftyzp7aN8tFd7zVlZSGIJWGGItB1vqz3ls5ynt6EGLk6SRPtsHCiunaIzYUCLl5c1kFyjyqW3Aab77TQL6sNeTEOb2nS9wDi4xEfS1yDcJIT3swupHf4+tHtTHBMqwtvTpqdyRwkoauAaP94qWP2Glz3YnEpgJFLfvI9MHlOG+dMs6iroJ6UTzNfYOeWKq7xXrmtH2DY0u93fa9JOFIe4CrcSEt88fSO4o1BcdnPitLoB5GfgC8G4IanmtPTMl/3lrnjA2LkhqNs4w4ad9Xhd0fMLRKDofKSRarhMmfMxRY8o14K/2AmUWoS6DBjiOoHap7UTwdPxh2RmEhctG9ufvaQOXD6LAT2pewx9I9Jdg/FrEF7pCnuJxV8jFn8CryQw9QFYAqvgesjJNuHhjfoQdtuqNAQZl6RjoD0lg5iTnr/iwxfE6ayPilKPw7bJNw+yPUgNC//0rCj/KGcOS+9Ho2eD48Qh0GWpOOYBHhxVoo9oF2M1evlMpuuMXfM5KT++XZbxUbDZj1L22eYQDKF0Cwaf4NLCJ0/K5Wwcjphshmk7iy2t5c+JL76T5lFTt/aYQMGNdMdqB/Ato8aow8=
|
||||||
script:
|
script:
|
||||||
- npm run lint
|
|
||||||
- npm test
|
|
||||||
- npm run build
|
- npm run build
|
||||||
|
before_deploy:
|
||||||
|
- npm install -g firebase-tools
|
||||||
deploy:
|
deploy:
|
||||||
provider: heroku
|
- provider: script
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
app: unpkg
|
script: $(npm bin -g)/firebase deploy
|
||||||
api_key:
|
--project unpkg-gcp
|
||||||
secure: qJTOiZE1hbghS0U7xpqU/38jm1Dga8bycsgDTW2wOSfAxqxtR4rAilW3ffBHY8/VSqt5GLkyPqFzb9R3I+xlcK6oakQyNMFGjQwMAqwdtHjucr45jWYNeqWf6Nbrrj+0LiQiH/uqMB4mKPquGqx2k2JIHW6jy5ndK14lI9a7SP44edyNugjhNW+OOYjwqo6maHvRoFNxL7kxrw7Js90d0gIKXpunBqHmxmr/hlYcGr9qJ+bLUXxmObxpoFWKHE0iTsTw3C3y558wZpHTfa3vXIvhseW2q7ATuRRVBv8StNjYxYSGpeZA+59d48z3EPK/mp7ybZO6cWyaBdcA9yF3thISEXlXcRcCck5KYk4gwzgrcfug59Z1VJUVXCywJTUu42V5ISoyUKJ+DelkkvMpXQH9T9fxm5eWEwb7sb+UTo3R8mlFliBogvXrWOKNXeRFsLQXH3/YmOUWONUUYRJS11K/p+sFOTfTfaH5lS0AFKCAKtvfLjdLUclsmmrITSFQa42/SRsFx88v3RxDL93r2838p51N7U/RqOqTNfQ8JR2iQOWlthMEvnVZMNs3VT9lW5eYIdTd5kpW/ARVtQf2drVVYR5BzNRJlGyw7M4x1riwNO7XhNqH76Iy/zMbeEDGaXqBCfzgq9vsRAjZm/TFKw8eOEy8+EGxLrBKD1U7EuI=
|
--message "https://travis-ci.com/$TRAVIS_REPO_SLUG/builds/$TRAVIS_BUILD_ID"
|
||||||
|
--token $FIREBASE_TOKEN
|
||||||
|
--non-interactive
|
||||||
|
--force
|
||||||
on:
|
on:
|
||||||
branch: master
|
branch: firebase-hosting
|
||||||
|
|
2
Procfile
2
Procfile
|
@ -1,2 +0,0 @@
|
||||||
web: node server.js
|
|
||||||
ingest_logs: node modules/ingestLogsEveryMinute.js
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"hosting": {
|
||||||
|
"public": "public",
|
||||||
|
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
|
||||||
|
"rewrites": [
|
||||||
|
{ "source": "/api/auth", "function": "serveAuth" },
|
||||||
|
{ "source": "/api/public-key", "function": "servePublicKey" },
|
||||||
|
{ "source": "/api/stats", "function": "serveStats" },
|
||||||
|
{ "source": "**/", "function": "serveAutoIndexPage" },
|
||||||
|
{ "source": "**", "function": "serveNpmPackageFile" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "functions",
|
||||||
|
"description": "Cloud Functions for Firebase",
|
||||||
|
"scripts": {
|
||||||
|
"serve": "firebase serve --only functions",
|
||||||
|
"shell": "firebase functions:shell",
|
||||||
|
"start": "npm run shell",
|
||||||
|
"deploy": "firebase deploy --only functions",
|
||||||
|
"logs": "firebase functions:log"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.2.2",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.2.3",
|
||||||
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||||
|
"@babel/plugin-syntax-export-default-from": "^7.2.0",
|
||||||
|
"@babel/plugin-syntax-export-namespace-from": "^7.2.0",
|
||||||
|
"@babel/plugin-syntax-import-meta": "^7.2.0",
|
||||||
|
"cheerio": "^1.0.0-rc.2",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"date-fns": "^1.30.1",
|
||||||
|
"etag": "^1.8.1",
|
||||||
|
"express": "^4.16.4",
|
||||||
|
"firebase-admin": "^6.0.0",
|
||||||
|
"firebase-functions": "^2.1.0",
|
||||||
|
"gunzip-maybe": "^1.4.1",
|
||||||
|
"invariant": "^2.2.4",
|
||||||
|
"isomorphic-fetch": "^2.2.1",
|
||||||
|
"jsonwebtoken": "^8.4.0",
|
||||||
|
"lru-cache": "^5.1.1",
|
||||||
|
"mime": "^2.4.0",
|
||||||
|
"ndjson": "^1.5.0",
|
||||||
|
"node-forge": "^0.7.6",
|
||||||
|
"pretty-bytes": "^5.1.0",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"react": "^16.7.0",
|
||||||
|
"react-dom": "^16.7.0",
|
||||||
|
"semver": "^5.6.0",
|
||||||
|
"sort-by": "^1.2.0",
|
||||||
|
"sri-toolbox": "^0.2.0",
|
||||||
|
"tar-stream": "^1.6.2",
|
||||||
|
"validate-npm-package-name": "^3.0.0",
|
||||||
|
"warning": "^4.0.2",
|
||||||
|
"whatwg-url": "^7.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"engines": {
|
||||||
|
"node": "8"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,173 +0,0 @@
|
||||||
const db = require('./utils/data');
|
|
||||||
|
|
||||||
const CloudflareAPI = require('./CloudflareAPI');
|
|
||||||
const BlacklistAPI = require('./BlacklistAPI');
|
|
||||||
|
|
||||||
function prunePackages(packagesMap) {
|
|
||||||
return Promise.all(
|
|
||||||
Object.keys(packagesMap).map(packageName =>
|
|
||||||
BlacklistAPI.includesPackage(packageName).then(blacklisted => {
|
|
||||||
if (blacklisted) {
|
|
||||||
delete packagesMap[packageName];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).then(() => packagesMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDayKey(date) {
|
|
||||||
return `${date.getUTCFullYear()}-${date.getUTCMonth()}-${date.getUTCDate()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createHourKey(date) {
|
|
||||||
return `${createDayKey(date)}-${date.getUTCHours()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMinuteKey(date) {
|
|
||||||
return `${createHourKey(date)}-${date.getUTCMinutes()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createScoresMap(array) {
|
|
||||||
const map = {};
|
|
||||||
|
|
||||||
for (let i = 0; i < array.length; i += 2) {
|
|
||||||
map[array[i]] = parseInt(array[i + 1], 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getScoresMap(key, n = 100) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
db.zrevrange(key, 0, n, 'withscores', (error, value) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
} else {
|
|
||||||
resolve(createScoresMap(value));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPackageRequests(date, n = 100) {
|
|
||||||
return getScoresMap(`stats-packageRequests-${createDayKey(date)}`, n).then(
|
|
||||||
prunePackages
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPackageBandwidth(date, n = 100) {
|
|
||||||
return getScoresMap(`stats-packageBytes-${createDayKey(date)}`, n).then(
|
|
||||||
prunePackages
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProtocolRequests(date) {
|
|
||||||
return getScoresMap(`stats-protocolRequests-${createDayKey(date)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDailyMetricsToTimeseries(timeseries) {
|
|
||||||
const since = new Date(timeseries.since);
|
|
||||||
|
|
||||||
return Promise.all([
|
|
||||||
getPackageRequests(since),
|
|
||||||
getPackageBandwidth(since),
|
|
||||||
getProtocolRequests(since)
|
|
||||||
]).then(results => {
|
|
||||||
timeseries.requests.package = results[0];
|
|
||||||
timeseries.bandwidth.package = results[1];
|
|
||||||
timeseries.requests.protocol = results[2];
|
|
||||||
return timeseries;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sumMaps(maps) {
|
|
||||||
return maps.reduce((memo, map) => {
|
|
||||||
Object.keys(map).forEach(key => {
|
|
||||||
memo[key] = (memo[key] || 0) + map[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
return memo;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDailyMetrics(result) {
|
|
||||||
return Promise.all(result.timeseries.map(addDailyMetricsToTimeseries)).then(
|
|
||||||
() => {
|
|
||||||
result.totals.requests.package = sumMaps(
|
|
||||||
result.timeseries.map(timeseries => {
|
|
||||||
return timeseries.requests.package;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
result.totals.bandwidth.package = sumMaps(
|
|
||||||
result.timeseries.map(timeseries => timeseries.bandwidth.package)
|
|
||||||
);
|
|
||||||
|
|
||||||
result.totals.requests.protocol = sumMaps(
|
|
||||||
result.timeseries.map(timeseries => timeseries.requests.protocol)
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractPublicInfo(data) {
|
|
||||||
return {
|
|
||||||
since: data.since,
|
|
||||||
until: data.until,
|
|
||||||
|
|
||||||
requests: {
|
|
||||||
all: data.requests.all,
|
|
||||||
cached: data.requests.cached,
|
|
||||||
country: data.requests.country,
|
|
||||||
status: data.requests.http_status
|
|
||||||
},
|
|
||||||
|
|
||||||
bandwidth: {
|
|
||||||
all: data.bandwidth.all,
|
|
||||||
cached: data.bandwidth.cached,
|
|
||||||
country: data.bandwidth.country
|
|
||||||
},
|
|
||||||
|
|
||||||
threats: {
|
|
||||||
all: data.threats.all,
|
|
||||||
country: data.threats.country
|
|
||||||
},
|
|
||||||
|
|
||||||
uniques: {
|
|
||||||
all: data.uniques.all
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const DomainNames = ['unpkg.com', 'npmcdn.com'];
|
|
||||||
|
|
||||||
function fetchStats(since, until) {
|
|
||||||
return CloudflareAPI.getZones(DomainNames).then(zones => {
|
|
||||||
return CloudflareAPI.getZoneAnalyticsDashboard(zones, since, until).then(
|
|
||||||
dashboard => {
|
|
||||||
return {
|
|
||||||
timeseries: dashboard.timeseries.map(extractPublicInfo),
|
|
||||||
totals: extractPublicInfo(dashboard.totals)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const oneMinute = 1000 * 60;
|
|
||||||
const oneHour = oneMinute * 60;
|
|
||||||
const oneDay = oneHour * 24;
|
|
||||||
|
|
||||||
function getStats(since, until) {
|
|
||||||
const promise = fetchStats(since, until);
|
|
||||||
return until - since > oneDay ? promise.then(addDailyMetrics) : promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
createDayKey,
|
|
||||||
createHourKey,
|
|
||||||
createMinuteKey,
|
|
||||||
getStats
|
|
||||||
};
|
|
|
@ -1 +1 @@
|
||||||
module.exports = {};
|
export default {};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const request = require('supertest');
|
import request from 'supertest';
|
||||||
|
|
||||||
const createServer = require('../createServer');
|
import createServer from '../createServer';
|
||||||
const withRevokedToken = require('./utils/withRevokedToken');
|
import withRevokedToken from './utils/withRevokedToken';
|
||||||
const withToken = require('./utils/withToken');
|
import withToken from './utils/withToken';
|
||||||
|
|
||||||
describe('The /_auth endpoint', () => {
|
describe('The /_auth endpoint', () => {
|
||||||
let server;
|
let server;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const request = require('supertest');
|
import request from 'supertest';
|
||||||
|
|
||||||
const createServer = require('../createServer');
|
import createServer from '../createServer';
|
||||||
const clearBlacklist = require('./utils/clearBlacklist');
|
import clearBlacklist from './utils/clearBlacklist';
|
||||||
const withToken = require('./utils/withToken');
|
import withToken from './utils/withToken';
|
||||||
|
|
||||||
describe('The /_blacklist endpoint', () => {
|
describe('The /_blacklist endpoint', () => {
|
||||||
let server;
|
let server;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const request = require('supertest');
|
import request from 'supertest';
|
||||||
|
|
||||||
const createServer = require('../createServer');
|
import createServer from '../createServer';
|
||||||
|
|
||||||
describe('The /_publicKey endpoint', () => {
|
describe('The /_publicKey endpoint', () => {
|
||||||
let server;
|
let server;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const request = require('supertest');
|
import request from 'supertest';
|
||||||
|
|
||||||
const createServer = require('../createServer');
|
import createServer from '../createServer';
|
||||||
const withAuthHeader = require('./utils/withAuthHeader');
|
import withAuthHeader from './utils/withAuthHeader';
|
||||||
const withRevokedToken = require('./utils/withRevokedToken');
|
import withRevokedToken from './utils/withRevokedToken';
|
||||||
const withToken = require('./utils/withToken');
|
import withToken from './utils/withToken';
|
||||||
|
|
||||||
describe('The /api/auth endpoint', () => {
|
describe('The /api/auth endpoint', () => {
|
||||||
let server;
|
let server;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const request = require('supertest');
|
import request from 'supertest';
|
||||||
|
|
||||||
const createServer = require('../createServer');
|
import createServer from '../createServer';
|
||||||
const clearBlacklist = require('./utils/clearBlacklist');
|
import clearBlacklist from './utils/clearBlacklist';
|
||||||
const withToken = require('./utils/withToken');
|
import withToken from './utils/withToken';
|
||||||
|
|
||||||
describe('The /api/blacklist endpoint', () => {
|
describe('The /api/blacklist endpoint', () => {
|
||||||
let server;
|
let server;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const request = require('supertest');
|
import request from 'supertest';
|
||||||
|
|
||||||
const createServer = require('../createServer');
|
import createServer from '../createServer';
|
||||||
|
|
||||||
describe('The /api/publicKey endpoint', () => {
|
describe('The /api/publicKey endpoint', () => {
|
||||||
let server;
|
let server;
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
const request = require('supertest');
|
import request from 'supertest';
|
||||||
|
|
||||||
const createServer = require('../createServer');
|
import createServer from '../createServer';
|
||||||
|
import clearBlacklist from './utils/clearBlacklist';
|
||||||
const clearBlacklist = require('./utils/clearBlacklist');
|
import withBlacklist from './utils/withBlacklist';
|
||||||
const withBlacklist = require('./utils/withBlacklist');
|
|
||||||
|
|
||||||
describe('The server', () => {
|
describe('The server', () => {
|
||||||
let server;
|
let server;
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
const closeDatabase = require('./utils/closeDatabase');
|
import closeDatabase from './utils/closeDatabase';
|
||||||
|
|
||||||
afterAll(closeDatabase);
|
afterAll(closeDatabase);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
const BlacklistAPI = require('../../BlacklistAPI');
|
import { removeAllPackages } from '../../utils/blacklist';
|
||||||
|
|
||||||
function clearBlacklist(done) {
|
export default function clearBlacklist(done) {
|
||||||
BlacklistAPI.removeAllPackages().then(done, done);
|
removeAllPackages().then(done, done);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = clearBlacklist;
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
const data = require('../../utils/data');
|
import data from '../../utils/data';
|
||||||
|
|
||||||
function closeDatabase() {
|
export default function closeDatabase() {
|
||||||
data.quit();
|
data.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = closeDatabase;
|
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
const withToken = require('./withToken');
|
import withToken from './withToken';
|
||||||
|
|
||||||
function encodeBase64(token) {
|
function encodeBase64(token) {
|
||||||
return Buffer.from(token).toString('base64');
|
return Buffer.from(token).toString('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
function withAuthHeader(scopes, done) {
|
export default function withAuthHeader(scopes, done) {
|
||||||
withToken(scopes, token => {
|
withToken(scopes, token => {
|
||||||
done(encodeBase64(token));
|
done(encodeBase64(token));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = withAuthHeader;
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
const BlacklistAPI = require('../../BlacklistAPI');
|
import { addPackage } from '../../utils/blacklist';
|
||||||
|
|
||||||
function withBlacklist(blacklist, done) {
|
export default function withBlacklist(blacklist, done) {
|
||||||
Promise.all(blacklist.map(BlacklistAPI.addPackage)).then(done);
|
Promise.all(blacklist.map(addPackage)).then(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = withBlacklist;
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
const withToken = require('./withToken');
|
import { revokeToken } from '../../utils/auth';
|
||||||
const AuthAPI = require('../../AuthAPI');
|
import withToken from './withToken';
|
||||||
|
|
||||||
function withRevokedToken(scopes, done) {
|
export default function withRevokedToken(scopes, done) {
|
||||||
withToken(scopes, token => {
|
withToken(scopes, token => {
|
||||||
AuthAPI.revokeToken(token).then(() => {
|
revokeToken(token).then(() => {
|
||||||
done(token);
|
done(token);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = withRevokedToken;
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
const AuthAPI = require('../../AuthAPI');
|
import { createToken } from '../../utils/auth';
|
||||||
|
|
||||||
function withToken(scopes, done) {
|
export default function withToken(scopes, done) {
|
||||||
AuthAPI.createToken(scopes).then(done);
|
createToken(scopes).then(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = withToken;
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const validateNpmPackageName = require('validate-npm-package-name');
|
import validateNpmPackageName from 'validate-npm-package-name';
|
||||||
|
|
||||||
const BlacklistAPI = require('../BlacklistAPI');
|
import { addPackage } from '../utils/blacklist';
|
||||||
|
|
||||||
function addToBlacklist(req, res) {
|
export default function addToBlacklist(req, res) {
|
||||||
const packageName = req.body.packageName;
|
const packageName = req.body.packageName;
|
||||||
|
|
||||||
if (!packageName) {
|
if (!packageName) {
|
||||||
|
@ -21,7 +21,7 @@ function addToBlacklist(req, res) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
BlacklistAPI.addPackage(packageName).then(
|
addPackage(packageName).then(
|
||||||
added => {
|
added => {
|
||||||
if (added) {
|
if (added) {
|
||||||
const userId = req.user.jti;
|
const userId = req.user.jti;
|
||||||
|
@ -45,5 +45,3 @@ function addToBlacklist(req, res) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = addToBlacklist;
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const AuthAPI = require('../AuthAPI');
|
import { createToken } from '../utils/auth';
|
||||||
|
|
||||||
const defaultScopes = {
|
const defaultScopes = {
|
||||||
blacklist: {
|
blacklist: {
|
||||||
|
@ -6,8 +6,8 @@ const defaultScopes = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function createAuth(req, res) {
|
export default function createAuth(req, res) {
|
||||||
AuthAPI.createToken(defaultScopes).then(
|
createToken(defaultScopes).then(
|
||||||
token => {
|
token => {
|
||||||
res.send({ token });
|
res.send({ token });
|
||||||
},
|
},
|
||||||
|
@ -20,5 +20,3 @@ function createAuth(req, res) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = createAuth;
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const validateNpmPackageName = require('validate-npm-package-name');
|
import validateNpmPackageName from 'validate-npm-package-name';
|
||||||
|
|
||||||
const BlacklistAPI = require('../BlacklistAPI');
|
import { removePackage } from '../utils/blacklist';
|
||||||
|
|
||||||
function removeFromBlacklist(req, res) {
|
export default function removeFromBlacklist(req, res) {
|
||||||
// TODO: Remove req.packageName when DELETE
|
// TODO: Remove req.packageName when DELETE
|
||||||
// /_blacklist/:packageName API is removed
|
// /_blacklist/:packageName API is removed
|
||||||
const packageName = req.body.packageName || req.packageName;
|
const packageName = req.body.packageName || req.packageName;
|
||||||
|
@ -23,7 +23,7 @@ function removeFromBlacklist(req, res) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
BlacklistAPI.removePackage(packageName).then(
|
removePackage(packageName).then(
|
||||||
removed => {
|
removed => {
|
||||||
if (removed) {
|
if (removed) {
|
||||||
const userId = req.user.jti;
|
const userId = req.user.jti;
|
||||||
|
@ -48,5 +48,3 @@ function removeFromBlacklist(req, res) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = removeFromBlacklist;
|
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const ReactDOMServer = require('react-dom/server');
|
import ReactDOMServer from 'react-dom/server';
|
||||||
const semver = require('semver');
|
import semver from 'semver';
|
||||||
|
|
||||||
const MainPage = require('../client/MainPage');
|
import MainPage from '../client/MainPage';
|
||||||
const AutoIndexApp = require('../client/autoIndex/App');
|
import AutoIndexApp from '../client/autoIndex/App';
|
||||||
const createHTML = require('../client/utils/createHTML');
|
import createHTML from '../client/utils/createHTML';
|
||||||
const renderPage = require('../utils/renderPage');
|
import renderPage from '../utils/renderPage';
|
||||||
|
|
||||||
const globalScripts =
|
const globalScripts =
|
||||||
process.env.NODE_ENV === 'production'
|
process.env.NODE_ENV === 'production'
|
||||||
? [
|
? [
|
||||||
'/react@16.4.1/umd/react.production.min.js',
|
'/react@16.7.0/umd/react.production.min.js',
|
||||||
'/react-dom@16.4.1/umd/react-dom.production.min.js'
|
'/react-dom@16.7.0/umd/react-dom.production.min.js'
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
'/react@16.4.1/umd/react.development.js',
|
'/react@16.7.0/umd/react.development.js',
|
||||||
'/react-dom@16.4.1/umd/react-dom.development.js'
|
'/react-dom@16.7.0/umd/react-dom.development.js'
|
||||||
];
|
];
|
||||||
|
|
||||||
function byVersion(a, b) {
|
function byVersion(a, b) {
|
||||||
return semver.lt(a, b) ? -1 : semver.gt(a, b) ? 1 : 0;
|
return semver.lt(a, b) ? -1 : semver.gt(a, b) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function serveAutoIndexPage(req, res) {
|
export default function serveAutoIndexPage(req, res) {
|
||||||
const scripts = globalScripts.concat(req.assets.getScripts('autoIndex'));
|
const scripts = globalScripts.concat('/_assets/autoIndex.js');
|
||||||
const styles = req.assets.getStyles('autoIndex');
|
const styles = ['/autoIndex.css'];
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
packageName: req.packageName,
|
packageName: req.packageName,
|
||||||
|
@ -54,5 +54,3 @@ function serveAutoIndexPage(req, res) {
|
||||||
})
|
})
|
||||||
.send(html);
|
.send(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = serveAutoIndexPage;
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
const serveAutoIndexPage = require('./serveAutoIndexPage');
|
import serveAutoIndexPage from './serveAutoIndexPage';
|
||||||
const serveHTMLModule = require('./serveHTMLModule');
|
import serveHTMLModule from './serveHTMLModule';
|
||||||
const serveJavaScriptModule = require('./serveJavaScriptModule');
|
import serveJavaScriptModule from './serveJavaScriptModule';
|
||||||
const serveStaticFile = require('./serveStaticFile');
|
import serveStaticFile from './serveStaticFile';
|
||||||
const serveMetadata = require('./serveMetadata');
|
import serveMetadata from './serveMetadata';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the file, JSON metadata, or HTML directory listing.
|
* Send the file, JSON metadata, or HTML directory listing.
|
||||||
*/
|
*/
|
||||||
function serveFile(req, res) {
|
export default function serveFile(req, res) {
|
||||||
if (req.query.meta != null) {
|
if (req.query.meta != null) {
|
||||||
return serveMetadata(req, res);
|
return serveMetadata(req, res);
|
||||||
}
|
}
|
||||||
|
@ -33,5 +33,3 @@ function serveFile(req, res) {
|
||||||
|
|
||||||
serveStaticFile(req, res);
|
serveStaticFile(req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = serveFile;
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
const etag = require('etag');
|
import etag from 'etag';
|
||||||
const cheerio = require('cheerio');
|
import cheerio from 'cheerio';
|
||||||
|
|
||||||
const getContentTypeHeader = require('../utils/getContentTypeHeader');
|
import getContentTypeHeader from '../utils/getContentTypeHeader';
|
||||||
const rewriteBareModuleIdentifiers = require('../utils/rewriteBareModuleIdentifiers');
|
import rewriteBareModuleIdentifiers from '../utils/rewriteBareModuleIdentifiers';
|
||||||
|
|
||||||
function serveHTMLModule(req, res) {
|
export default function serveHTMLModule(req, res) {
|
||||||
try {
|
try {
|
||||||
const $ = cheerio.load(req.entry.content.toString('utf8'));
|
const $ = cheerio.load(req.entry.content.toString('utf8'));
|
||||||
|
|
||||||
|
@ -46,5 +46,3 @@ function serveHTMLModule(req, res) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = serveHTMLModule;
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const etag = require('etag');
|
import etag from 'etag';
|
||||||
|
|
||||||
const getContentTypeHeader = require('../utils/getContentTypeHeader');
|
import getContentTypeHeader from '../utils/getContentTypeHeader';
|
||||||
const rewriteBareModuleIdentifiers = require('../utils/rewriteBareModuleIdentifiers');
|
import rewriteBareModuleIdentifiers from '../utils/rewriteBareModuleIdentifiers';
|
||||||
|
|
||||||
function serveJavaScriptModule(req, res) {
|
export default function serveJavaScriptModule(req, res) {
|
||||||
try {
|
try {
|
||||||
const code = rewriteBareModuleIdentifiers(
|
const code = rewriteBareModuleIdentifiers(
|
||||||
req.entry.content.toString('utf8'),
|
req.entry.content.toString('utf8'),
|
||||||
|
@ -40,5 +40,3 @@ function serveJavaScriptModule(req, res) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = serveJavaScriptModule;
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
|
|
||||||
const addLeadingSlash = require('../utils/addLeadingSlash');
|
import addLeadingSlash from '../utils/addLeadingSlash';
|
||||||
|
|
||||||
function getMatchingEntries(entry, entries) {
|
function getMatchingEntries(entry, entries) {
|
||||||
const dirname = entry.name || '.';
|
const dirname = entry.name || '.';
|
||||||
|
@ -30,7 +30,7 @@ function getMetadata(entry, entries) {
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
function serveMetadata(req, res) {
|
export default function serveMetadata(req, res) {
|
||||||
const metadata = getMetadata(req.entry, req.entries);
|
const metadata = getMetadata(req.entry, req.entries);
|
||||||
|
|
||||||
res
|
res
|
||||||
|
@ -40,5 +40,3 @@ function serveMetadata(req, res) {
|
||||||
})
|
})
|
||||||
.send(metadata);
|
.send(metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = serveMetadata;
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
const MainPage = require('../client/MainPage');
|
|
||||||
const renderPage = require('../utils/renderPage');
|
|
||||||
|
|
||||||
const globalScripts =
|
|
||||||
process.env.NODE_ENV === 'production'
|
|
||||||
? [
|
|
||||||
'/react@16.4.1/umd/react.production.min.js',
|
|
||||||
'/react-dom@16.4.1/umd/react-dom.production.min.js',
|
|
||||||
'/react-router-dom@4.3.1/umd/react-router-dom.min.js'
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
'/react@16.4.1/umd/react.development.js',
|
|
||||||
'/react-dom@16.4.1/umd/react-dom.development.js',
|
|
||||||
'/react-router-dom@4.3.1/umd/react-router-dom.js'
|
|
||||||
];
|
|
||||||
|
|
||||||
function serveRootPage(req, res) {
|
|
||||||
const scripts = globalScripts.concat(req.assets.getScripts('main'));
|
|
||||||
const styles = req.assets.getStyles('main');
|
|
||||||
|
|
||||||
const html = renderPage(MainPage, {
|
|
||||||
scripts: scripts,
|
|
||||||
styles: styles
|
|
||||||
});
|
|
||||||
|
|
||||||
res.send(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = serveRootPage;
|
|
|
@ -1,9 +1,9 @@
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const etag = require('etag');
|
import etag from 'etag';
|
||||||
|
|
||||||
const getContentTypeHeader = require('../utils/getContentTypeHeader');
|
import getContentTypeHeader from '../utils/getContentTypeHeader';
|
||||||
|
|
||||||
function serveStaticFile(req, res) {
|
export default function serveStaticFile(req, res) {
|
||||||
const tags = ['file'];
|
const tags = ['file'];
|
||||||
|
|
||||||
const ext = path.extname(req.entry.name).substr(1);
|
const ext = path.extname(req.entry.name).substr(1);
|
||||||
|
@ -22,5 +22,3 @@ function serveStaticFile(req, res) {
|
||||||
})
|
})
|
||||||
.send(req.entry.content);
|
.send(req.entry.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = serveStaticFile;
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
function showAuth(req, res) {
|
export default function showAuth(req, res) {
|
||||||
res.send({ auth: req.user });
|
res.send({ auth: req.user });
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = showAuth;
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const BlacklistAPI = require('../BlacklistAPI');
|
import { getPackages } from '../utils/blacklist';
|
||||||
|
|
||||||
function showBlacklist(req, res) {
|
export default function showBlacklist(req, res) {
|
||||||
BlacklistAPI.getPackages().then(
|
getPackages().then(
|
||||||
blacklist => {
|
blacklist => {
|
||||||
res.send({ blacklist });
|
res.send({ blacklist });
|
||||||
},
|
},
|
||||||
|
@ -13,5 +13,3 @@ function showBlacklist(req, res) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = showBlacklist;
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
const secretKey = require('../secretKey');
|
import secretKey from '../secretKey';
|
||||||
|
|
||||||
function showPublicKey(req, res) {
|
export default function showPublicKey(req, res) {
|
||||||
res.send({ publicKey: secretKey.public });
|
res.send({ publicKey: secretKey.public });
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = showPublicKey;
|
|
||||||
|
|
|
@ -1,29 +1,37 @@
|
||||||
const subDays = require('date-fns/sub_days');
|
import { subDays, startOfDay, startOfSecond } from 'date-fns';
|
||||||
const startOfDay = require('date-fns/start_of_day');
|
|
||||||
const startOfSecond = require('date-fns/start_of_second');
|
|
||||||
|
|
||||||
const StatsAPI = require('../StatsAPI');
|
import { getStats } from '../utils/stats';
|
||||||
|
|
||||||
function showStats(req, res) {
|
export default function showStats(req, res) {
|
||||||
let since, until;
|
let since, until;
|
||||||
switch (req.query.period) {
|
if (req.query.period) {
|
||||||
case 'last-day':
|
switch (req.query.period) {
|
||||||
until = startOfDay(new Date());
|
case 'last-day':
|
||||||
since = subDays(until, 1);
|
until = startOfDay(new Date());
|
||||||
break;
|
since = subDays(until, 1);
|
||||||
case 'last-week':
|
break;
|
||||||
until = startOfDay(new Date());
|
case 'last-week':
|
||||||
since = subDays(until, 7);
|
until = startOfDay(new Date());
|
||||||
break;
|
since = subDays(until, 7);
|
||||||
case 'last-month':
|
break;
|
||||||
until = startOfDay(new Date());
|
case 'last-month':
|
||||||
since = subDays(until, 30);
|
default:
|
||||||
break;
|
until = startOfDay(new Date());
|
||||||
default:
|
since = subDays(until, 30);
|
||||||
until = req.query.until
|
}
|
||||||
? new Date(req.query.until)
|
} else {
|
||||||
: startOfSecond(new Date());
|
if (!req.query.since) {
|
||||||
since = new Date(req.query.since);
|
return res.status(403).send({ error: 'Missing ?since query parameter' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.query.until) {
|
||||||
|
return res.status(403).send({ error: 'Missing ?until query parameter' });
|
||||||
|
}
|
||||||
|
|
||||||
|
since = new Date(req.query.since);
|
||||||
|
until = req.query.until
|
||||||
|
? new Date(req.query.until)
|
||||||
|
: startOfSecond(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN(since.getTime())) {
|
if (isNaN(since.getTime())) {
|
||||||
|
@ -44,7 +52,7 @@ function showStats(req, res) {
|
||||||
return res.status(403).send({ error: '?until must be a date in the past' });
|
return res.status(403).send({ error: '?until must be a date in the past' });
|
||||||
}
|
}
|
||||||
|
|
||||||
StatsAPI.getStats(since, until).then(
|
getStats(since, until).then(
|
||||||
stats => {
|
stats => {
|
||||||
res
|
res
|
||||||
.set({
|
.set({
|
||||||
|
@ -59,5 +67,3 @@ function showStats(req, res) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = showStats;
|
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
"plugin:react/recommended"
|
"plugin:react/recommended"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"react/no-children-prop": 0
|
"import/no-unresolved": 0,
|
||||||
|
"react/no-children-prop": 0,
|
||||||
|
"react/prop-types": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,30 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const PropTypes = require('prop-types');
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const createHTML = require('./utils/createHTML');
|
import createHTML from './utils/createHTML';
|
||||||
const x = require('./utils/execScript');
|
import x from './utils/execScript';
|
||||||
|
|
||||||
function MainPage({ title, description, scripts, styles, data, content }) {
|
export default function MainPage({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
favicon,
|
||||||
|
scripts,
|
||||||
|
styles,
|
||||||
|
data,
|
||||||
|
content
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charSet="utf-8" />
|
<meta charSet="utf-8" />
|
||||||
<meta name="description" content={description} />
|
|
||||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
<meta httpEquiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
|
{description && <meta name="description" content={description} />}
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width,initial-scale=1,maximum-scale=1"
|
content="width=device-width,initial-scale=1,maximum-scale=1"
|
||||||
/>
|
/>
|
||||||
<meta name="timestamp" content={new Date().toISOString()} />
|
<meta name="timestamp" content={new Date().toISOString()} />
|
||||||
<link rel="shortcut icon" href="/favicon.ico" />
|
{favicon && <link rel="shortcut icon" href={favicon} />}
|
||||||
{styles.map(s => (
|
{styles.map(s => (
|
||||||
<link key={s} rel="stylesheet" href={s} />
|
<link key={s} rel="stylesheet" href={s} />
|
||||||
))}
|
))}
|
||||||
|
@ -39,6 +47,16 @@ function MainPage({ title, description, scripts, styles, data, content }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MainPage.defaultProps = {
|
||||||
|
title: 'UNPKG',
|
||||||
|
description: 'The CDN for everything on npm',
|
||||||
|
favicon: '/favicon.ico',
|
||||||
|
scripts: [],
|
||||||
|
styles: [],
|
||||||
|
data: {},
|
||||||
|
content: createHTML('')
|
||||||
|
};
|
||||||
|
|
||||||
const htmlType = PropTypes.shape({
|
const htmlType = PropTypes.shape({
|
||||||
__html: PropTypes.string
|
__html: PropTypes.string
|
||||||
});
|
});
|
||||||
|
@ -46,19 +64,9 @@ const htmlType = PropTypes.shape({
|
||||||
MainPage.propTypes = {
|
MainPage.propTypes = {
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
description: PropTypes.string,
|
description: PropTypes.string,
|
||||||
|
favicon: PropTypes.string,
|
||||||
scripts: PropTypes.arrayOf(PropTypes.string),
|
scripts: PropTypes.arrayOf(PropTypes.string),
|
||||||
styles: PropTypes.arrayOf(PropTypes.string),
|
styles: PropTypes.arrayOf(PropTypes.string),
|
||||||
data: PropTypes.any,
|
data: PropTypes.any,
|
||||||
content: htmlType
|
content: htmlType
|
||||||
};
|
};
|
||||||
|
|
||||||
MainPage.defaultProps = {
|
|
||||||
title: 'UNPKG',
|
|
||||||
description: 'The CDN for everything on npm',
|
|
||||||
scripts: [],
|
|
||||||
styles: [],
|
|
||||||
data: {},
|
|
||||||
content: createHTML('')
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = MainPage;
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
require('./autoIndex.css');
|
// import './autoIndex.css';
|
||||||
|
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const ReactDOM = require('react-dom');
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
const App = require('./autoIndex/App');
|
import App from './autoIndex/App';
|
||||||
|
|
||||||
const props = window.__DATA__ || {};
|
const props = window.__DATA__ || {};
|
||||||
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
.app {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-version-selector {
|
|
||||||
line-height: 2.25em;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
.app-version-selector select {
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-address {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
|
@ -1,10 +1,43 @@
|
||||||
require('./App.css');
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const React = require('react');
|
import DirectoryListing from './DirectoryListing';
|
||||||
|
|
||||||
const DirectoryListing = require('./DirectoryListing');
|
const styles = {
|
||||||
|
wrapper: {
|
||||||
|
maxWidth: 900,
|
||||||
|
margin: '0 auto'
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
},
|
||||||
|
versionSelector: {
|
||||||
|
float: 'right',
|
||||||
|
lineHeight: '2.25em'
|
||||||
|
},
|
||||||
|
versionDropdown: {
|
||||||
|
fontSize: '1em'
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
textAlign: 'right'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const entryType = PropTypes.object;
|
||||||
|
|
||||||
|
export default class App extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
packageName: PropTypes.string.isRequired,
|
||||||
|
packageVersion: PropTypes.string.isRequired,
|
||||||
|
availableVersions: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
filename: PropTypes.string.isRequired,
|
||||||
|
entry: entryType.isRequired,
|
||||||
|
entries: PropTypes.objectOf(entryType).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
class App extends React.Component {
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
availableVersions: []
|
availableVersions: []
|
||||||
};
|
};
|
||||||
|
@ -18,19 +51,20 @@ class App extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div style={styles.wrapper}>
|
||||||
<header className="app-header">
|
<header style={styles.header}>
|
||||||
<h1>
|
<h1>
|
||||||
Index of /{this.props.packageName}@{this.props.packageVersion}
|
Index of /{this.props.packageName}@{this.props.packageVersion}
|
||||||
{this.props.filename}
|
{this.props.filename}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div className="app-version-selector">
|
<div style={styles.versionSelector}>
|
||||||
Version:{' '}
|
Version:{' '}
|
||||||
<select
|
<select
|
||||||
id="version"
|
id="version"
|
||||||
defaultValue={this.props.packageVersion}
|
defaultValue={this.props.packageVersion}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
|
style={styles.versionDropdown}
|
||||||
>
|
>
|
||||||
{this.props.availableVersions.map(v => (
|
{this.props.availableVersions.map(v => (
|
||||||
<option key={v} value={v}>
|
<option key={v} value={v}>
|
||||||
|
@ -51,27 +85,10 @@ class App extends React.Component {
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<address className="app-address">
|
<address style={styles.address}>
|
||||||
{this.props.packageName}@{this.props.packageVersion}
|
{this.props.packageName}@{this.props.packageVersion}
|
||||||
</address>
|
</address>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
const PropTypes = require('prop-types');
|
|
||||||
|
|
||||||
const entryType = PropTypes.object;
|
|
||||||
|
|
||||||
App.propTypes = {
|
|
||||||
packageName: PropTypes.string.isRequired,
|
|
||||||
packageVersion: PropTypes.string.isRequired,
|
|
||||||
availableVersions: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
filename: PropTypes.string.isRequired,
|
|
||||||
entry: entryType.isRequired,
|
|
||||||
entries: PropTypes.objectOf(entryType).isRequired
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = App;
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
.directory-listing table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
font: 0.85em Monaco, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.directory-listing tr.even {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.directory-listing th {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.directory-listing th,
|
|
||||||
.directory-listing td {
|
|
||||||
padding: 0.5em 1em;
|
|
||||||
}
|
|
|
@ -1,8 +1,7 @@
|
||||||
require('./DirectoryListing.css');
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const React = require('react');
|
import formatBytes from 'pretty-bytes';
|
||||||
const formatBytes = require('pretty-bytes');
|
import sortBy from 'sort-by';
|
||||||
const sortBy = require('sort-by');
|
|
||||||
|
|
||||||
function getDirname(name) {
|
function getDirname(name) {
|
||||||
return (
|
return (
|
||||||
|
@ -25,20 +24,38 @@ function getRelativeName(base, name) {
|
||||||
return base.length ? name.substr(base.length + 1) : name;
|
return base.length ? name.substr(base.length + 1) : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DirectoryListing({ filename, entry, entries }) {
|
const styles = {
|
||||||
|
table: {
|
||||||
|
width: '100%',
|
||||||
|
borderCollapse: 'collapse',
|
||||||
|
font: '0.85em Monaco, monospace'
|
||||||
|
},
|
||||||
|
evenRow: {
|
||||||
|
backgroundColor: '#eee'
|
||||||
|
},
|
||||||
|
tableHead: {
|
||||||
|
textAlign: 'left',
|
||||||
|
padding: '0.5em 1em'
|
||||||
|
},
|
||||||
|
tableCell: {
|
||||||
|
padding: '0.5em 1em'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function DirectoryListing({ filename, entry, entries }) {
|
||||||
const rows = [];
|
const rows = [];
|
||||||
|
|
||||||
if (filename !== '/') {
|
if (filename !== '/') {
|
||||||
rows.push(
|
rows.push(
|
||||||
<tr key="..">
|
<tr key="..">
|
||||||
<td>
|
<td style={styles.tableCell}>
|
||||||
<a title="Parent directory" href="../">
|
<a title="Parent directory" href="../">
|
||||||
..
|
..
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>-</td>
|
<td style={styles.tableCell}>-</td>
|
||||||
<td>-</td>
|
<td style={styles.tableCell}>-</td>
|
||||||
<td>-</td>
|
<td style={styles.tableCell}>-</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -54,14 +71,14 @@ function DirectoryListing({ filename, entry, entries }) {
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
<tr key={name}>
|
<tr key={name}>
|
||||||
<td>
|
<td style={styles.tableCell}>
|
||||||
<a title={relName} href={href}>
|
<a title={relName} href={href}>
|
||||||
{href}
|
{href}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>-</td>
|
<td style={styles.tableCell}>-</td>
|
||||||
<td>-</td>
|
<td style={styles.tableCell}>-</td>
|
||||||
<td>-</td>
|
<td style={styles.tableCell}>-</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -74,33 +91,33 @@ function DirectoryListing({ filename, entry, entries }) {
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
<tr key={name}>
|
<tr key={name}>
|
||||||
<td>
|
<td style={styles.tableCell}>
|
||||||
<a title={relName} href={relName}>
|
<a title={relName} href={relName}>
|
||||||
{relName}
|
{relName}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{contentType}</td>
|
<td style={styles.tableCell}>{contentType}</td>
|
||||||
<td>{formatBytes(size)}</td>
|
<td style={styles.tableCell}>{formatBytes(size)}</td>
|
||||||
<td>{lastModified}</td>
|
<td style={styles.tableCell}>{lastModified}</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="directory-listing">
|
<div>
|
||||||
<table>
|
<table style={styles.table}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th style={styles.tableHead}>Name</th>
|
||||||
<th>Type</th>
|
<th style={styles.tableHead}>Type</th>
|
||||||
<th>Size</th>
|
<th style={styles.tableHead}>Size</th>
|
||||||
<th>Last Modified</th>
|
<th style={styles.tableHead}>Last Modified</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{rows.map((row, index) =>
|
{rows.map((row, index) =>
|
||||||
React.cloneElement(row, {
|
React.cloneElement(row, {
|
||||||
className: index % 2 ? 'odd' : 'even'
|
style: index % 2 ? undefined : styles.evenRow
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -109,18 +126,12 @@ function DirectoryListing({ filename, entry, entries }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
const entryType = PropTypes.shape({
|
||||||
const PropTypes = require('prop-types');
|
name: PropTypes.string.isRequired
|
||||||
|
});
|
||||||
|
|
||||||
const entryType = PropTypes.shape({
|
DirectoryListing.propTypes = {
|
||||||
name: PropTypes.string.isRequired
|
filename: PropTypes.string.isRequired,
|
||||||
});
|
entry: entryType.isRequired,
|
||||||
|
entries: PropTypes.objectOf(entryType).isRequired
|
||||||
DirectoryListing.propTypes = {
|
};
|
||||||
filename: PropTypes.string.isRequired,
|
|
||||||
entry: entryType.isRequired,
|
|
||||||
entries: PropTypes.objectOf(entryType).isRequired
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = DirectoryListing;
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
require('./main.css');
|
// import './main.css';
|
||||||
|
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const ReactDOM = require('react-dom');
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
const App = require('./main/App');
|
import App from './main/App';
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.getElementById('root'));
|
ReactDOM.render(<App />, document.getElementById('root'));
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
.about-logos {
|
|
||||||
margin: 2em 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.about-logo {
|
|
||||||
text-align: center;
|
|
||||||
flex: 1;
|
|
||||||
max-width: 80%;
|
|
||||||
}
|
|
||||||
.about-logo img {
|
|
||||||
max-width: 60%;
|
|
||||||
}
|
|
|
@ -1,12 +1,95 @@
|
||||||
require('./About.css');
|
import React from 'react';
|
||||||
|
|
||||||
const React = require('react');
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
const h = require('../utils/createHTML');
|
import cloudflareLogo from './CloudflareLogo.png';
|
||||||
const markup = require('./About.md');
|
import herokuLogo from './HerokuLogo.png';
|
||||||
|
|
||||||
function About() {
|
const styles = {
|
||||||
return <div className="wrapper" dangerouslySetInnerHTML={h(markup)} />;
|
logoList: {
|
||||||
|
margin: '2em 0',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
textAlign: 'center',
|
||||||
|
flex: '1',
|
||||||
|
maxWidth: '80%'
|
||||||
|
},
|
||||||
|
logoImage: {
|
||||||
|
maxWidth: '60%'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function AboutLogo({ children }) {
|
||||||
|
return <div style={styles.logo}>{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = About;
|
function AboutLogoImage(props) {
|
||||||
|
return <img {...props} style={styles.logoImage} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function About() {
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<p>
|
||||||
|
unpkg is an <a href="https://github.com/unpkg">open source</a> project
|
||||||
|
built and maintained by{' '}
|
||||||
|
<a href="https://twitter.com/mjackson">Michael Jackson</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="sponsors">Sponsors</h3>
|
||||||
|
<p>
|
||||||
|
The fast, global infrastructure that powers unpkg is generously donated
|
||||||
|
by <a href="https://www.cloudflare.com">Cloudflare</a> and{' '}
|
||||||
|
<a href="https://www.heroku.com">Heroku</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style={styles.logoList}>
|
||||||
|
<AboutLogo>
|
||||||
|
<a href="https://www.cloudflare.com">
|
||||||
|
<AboutLogoImage src={cloudflareLogo} />
|
||||||
|
</a>
|
||||||
|
</AboutLogo>
|
||||||
|
<AboutLogo>
|
||||||
|
<a href="https://www.heroku.com">
|
||||||
|
<AboutLogoImage src={herokuLogo} />
|
||||||
|
</a>
|
||||||
|
</AboutLogo>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 id="cache-behavior">Cache Behavior</h3>
|
||||||
|
<p>
|
||||||
|
The CDN caches files based on their permanent URL, which includes the
|
||||||
|
npm package version. This works because npm does not allow package
|
||||||
|
authors to overwrite a package that has already been published with a
|
||||||
|
different one at the same version number.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
URLs that do not specify a package version number redirect to one that
|
||||||
|
does. This is the <code>latest</code> version when no version is
|
||||||
|
specified, or the <code>maxSatisfying</code> version when a{' '}
|
||||||
|
<a href="https://github.com/npm/node-semver">semver version</a> is
|
||||||
|
given. Redirects are cached for 5 minutes.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Browsers are instructed (via the <code>Cache-Control</code> header) to
|
||||||
|
cache assets for 1 year.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="abuse">Abuse</h3>
|
||||||
|
<p>
|
||||||
|
unpkg maintains a list of packages that are known to be malicious. If
|
||||||
|
you find such a package on npm, please let us know!
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="support">Support</h3>
|
||||||
|
<p>
|
||||||
|
unpkg is not affiliated with or supported by npm, Inc. in any way.
|
||||||
|
Please do not contact npm for help with unpkg. Instead, please reach out
|
||||||
|
to <a href="https://twitter.com/unpkg">@unpkg</a> with any questions or
|
||||||
|
concerns.
|
||||||
|
</p>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
unpkg is an [open source](https://github.com/unpkg) project built and maintained by [Michael Jackson](https://twitter.com/mjackson).
|
|
||||||
|
|
||||||
### Sponsors
|
|
||||||
|
|
||||||
The fast, global infrastructure that powers unpkg is generously donated by [Cloudflare](https://www.cloudflare.com) and [Heroku](https://www.heroku.com).
|
|
||||||
|
|
||||||
<div class="about-logos">
|
|
||||||
<div class="about-logo">
|
|
||||||
<a href="https://www.cloudflare.com"><img src="CloudflareLogo.png"></a>
|
|
||||||
</div>
|
|
||||||
<div class="about-logo">
|
|
||||||
<a href="https://www.heroku.com"><img src="HerokuLogo.png"></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
### Cache Behavior
|
|
||||||
|
|
||||||
The CDN caches files based on their permanent URL, which includes the npm package version. This works because npm does not allow package authors to overwrite a package that has already been published with a different one at the same version number.
|
|
||||||
|
|
||||||
URLs that do not specify a package version number redirect to one that does. This is the `latest` version when no version is specified, or the `maxSatisfying` version when a [semver version](https://github.com/npm/node-semver) is given. Redirects are cached for 5 minutes.
|
|
||||||
|
|
||||||
Browsers are instructed (via the `Cache-Control` header) to cache assets for 1 year.
|
|
||||||
|
|
||||||
### Abuse
|
|
||||||
|
|
||||||
unpkg maintains a list of packages that are known to be malicious. If you find such a package on npm, please let us know!
|
|
||||||
|
|
||||||
### Support
|
|
||||||
|
|
||||||
unpkg is not affiliated with or supported by npm, Inc. in any way. Please do not contact npm for help with unpkg. Instead, please reach out to [@unpkg](https://twitter.com/unpkg) with any questions or concerns.
|
|
|
@ -1,7 +1,7 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const { HashRouter } = require('react-router-dom');
|
import { HashRouter } from 'react-router-dom';
|
||||||
|
|
||||||
const Layout = require('./Layout');
|
import Layout from './Layout';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -11,4 +11,4 @@ function App() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = App;
|
export default App;
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
.home-example {
|
|
||||||
text-align: center;
|
|
||||||
background-color: #eee;
|
|
||||||
margin: 2em 0;
|
|
||||||
padding: 5px 0;
|
|
||||||
}
|
|
|
@ -1,12 +1,159 @@
|
||||||
require('./Home.css');
|
import React from 'react';
|
||||||
|
|
||||||
const React = require('react');
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
const h = require('../utils/createHTML');
|
const styles = {
|
||||||
const markup = require('./Home.md');
|
homeExample: {
|
||||||
|
textAlign: 'center',
|
||||||
|
backgroundColor: '#eee',
|
||||||
|
margin: '2em 0',
|
||||||
|
padding: '5px 0'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function Home() {
|
export default function Home() {
|
||||||
return <div className="wrapper" dangerouslySetInnerHTML={h(markup)} />;
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<p>
|
||||||
|
unpkg is a fast, global{' '}
|
||||||
|
<a href="https://en.wikipedia.org/wiki/Content_delivery_network">
|
||||||
|
content delivery network
|
||||||
|
</a>{' '}
|
||||||
|
for everything on <a href="https://www.npmjs.com/">npm</a>. Use it to
|
||||||
|
quickly and easily load any file from any package using a URL like:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style={styles.homeExample}>unpkg.com/:package@:version/:file</div>
|
||||||
|
|
||||||
|
<h3>Examples</h3>
|
||||||
|
|
||||||
|
<p>Using a fixed version:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/react@16.0.0/umd/react.production.min.js">
|
||||||
|
unpkg.com/react@16.0.0/umd/react.production.min.js
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/react-dom@16.0.0/umd/react-dom.production.min.js">
|
||||||
|
unpkg.com/react-dom@16.0.0/umd/react-dom.production.min.js
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You may also use a{' '}
|
||||||
|
<a href="https://docs.npmjs.com/misc/semver">semver range</a> or a{' '}
|
||||||
|
<a href="https://docs.npmjs.com/cli/dist-tag">tag</a> instead of a fixed
|
||||||
|
version number, or omit the version/tag entirely to use the{' '}
|
||||||
|
<code>latest</code> tag.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/react@^16/umd/react.production.min.js">
|
||||||
|
unpkg.com/react@^16/umd/react.production.min.js
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/react/umd/react.production.min.js">
|
||||||
|
unpkg.com/react/umd/react.production.min.js
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If you omit the file path (i.e. use a “bare” URL), unpkg
|
||||||
|
will serve the file specified by the <code>unpkg</code> field in{' '}
|
||||||
|
<code>package.json</code>, or fall back to
|
||||||
|
<code>main</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/d3">unpkg.com/d3</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/jquery">unpkg.com/jquery</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/three">unpkg.com/three</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Append a <code>/</code> at the end of a URL to view a listing of all the
|
||||||
|
files in a package.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/react/">unpkg.com/react/</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/lodash/">unpkg.com/lodash/</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Query Parameters</h3>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>
|
||||||
|
<code>?meta</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
Return metadata about any file in a package as JSON (e.g.
|
||||||
|
<code>/any/file?meta</code>)
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dt>
|
||||||
|
<code>?module</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
Expands all{' '}
|
||||||
|
<a href="https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier">
|
||||||
|
“bare” <code>import</code> specifiers
|
||||||
|
</a>
|
||||||
|
in JavaScript modules to unpkg URLs. This feature is{' '}
|
||||||
|
<em>very experimental</em>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Workflow</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
For npm package authors, unpkg relieves the burden of publishing your
|
||||||
|
code to a CDN in addition to the npm registry. All you need to do is
|
||||||
|
include your <a href="https://github.com/umdjs/umd">UMD</a> build in
|
||||||
|
your npm package (not your repo, that's different!).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>You can do this easily using the following setup:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Add the <code>umd</code> (or <code>dist</code>) directory to your{' '}
|
||||||
|
<code>.gitignore</code> file
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Add the <code>umd</code> directory to your{' '}
|
||||||
|
<a href="https://docs.npmjs.com/files/package.json#files">
|
||||||
|
files array
|
||||||
|
</a>{' '}
|
||||||
|
in
|
||||||
|
<code>package.json</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Use a build script to generate your UMD build in the <code>umd</code>{' '}
|
||||||
|
directory when you publish
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
That's it! Now when you <code>npm publish</code> you'll have a
|
||||||
|
version available on unpkg as well.
|
||||||
|
</p>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Home;
|
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
unpkg is a fast, global [content delivery network](https://en.wikipedia.org/wiki/Content_delivery_network) for everything on [npm](https://www.npmjs.com/). Use it to quickly and easily load any file from any package using a URL like:
|
|
||||||
|
|
||||||
<div class="home-example">unpkg.com/:package@:version/:file</div>
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
Using a fixed version:
|
|
||||||
|
|
||||||
* [unpkg.com/react@16.0.0/umd/react.production.min.js](/react@16.0.0/umd/react.production.min.js)
|
|
||||||
* [unpkg.com/react-dom@16.0.0/umd/react-dom.production.min.js](/react-dom@16.0.0/umd/react-dom.production.min.js)
|
|
||||||
|
|
||||||
You may also use a [semver range](https://docs.npmjs.com/misc/semver) or a [tag](https://docs.npmjs.com/cli/dist-tag) instead of a fixed version number, or omit the version/tag entirely to use the `latest` tag.
|
|
||||||
|
|
||||||
* [unpkg.com/react@^16/umd/react.production.min.js](/react@^16/umd/react.production.min.js)
|
|
||||||
* [unpkg.com/react/umd/react.production.min.js](/react/umd/react.production.min.js)
|
|
||||||
|
|
||||||
If you omit the file path (i.e. use a "bare" URL), unpkg will serve the file specified by the `unpkg` field in `package.json`, or fall back to `main`.
|
|
||||||
|
|
||||||
* [unpkg.com/d3](/d3)
|
|
||||||
* [unpkg.com/jquery](/jquery)
|
|
||||||
* [unpkg.com/three](/three)
|
|
||||||
|
|
||||||
Append a `/` at the end of a URL to view a listing of all the files in a package.
|
|
||||||
|
|
||||||
* [unpkg.com/react/](/react/)
|
|
||||||
* [unpkg.com/lodash/](/lodash/)
|
|
||||||
|
|
||||||
### Query Parameters
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>`?meta`</dt>
|
|
||||||
<dd>Return metadata about any file in a package as JSON (e.g. `/any/file?meta`)</dd>
|
|
||||||
|
|
||||||
<dt>`?module`</dt>
|
|
||||||
<dd>Expands all ["bare" `import` specifiers](https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier) in JavaScript modules to unpkg URLs. This feature is *very experimental*</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
### Workflow
|
|
||||||
|
|
||||||
For npm package authors, unpkg relieves the burden of publishing your code to a CDN in addition to the npm registry. All you need to do is include your [UMD](https://github.com/umdjs/umd) build in your npm package (not your repo, that's different!).
|
|
||||||
|
|
||||||
You can do this easily using the following setup:
|
|
||||||
|
|
||||||
* Add the `umd` (or `dist`) directory to your `.gitignore` file
|
|
||||||
* Add the `umd` directory to your [files array](https://docs.npmjs.com/files/package.json#files) in `package.json`
|
|
||||||
* Use a build script to generate your UMD build in the `umd` directory when you publish
|
|
||||||
|
|
||||||
That's it! Now when you `npm publish` you'll have a version available on unpkg as well.
|
|
|
@ -1,38 +0,0 @@
|
||||||
.layout-title {
|
|
||||||
margin: 0;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-nav {
|
|
||||||
margin: 0 0 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-navList {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.layout-navList li {
|
|
||||||
flex-basis: auto;
|
|
||||||
list-style-type: none;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 1.1em;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
.layout-navList li a:link {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.layout-navList li a:link,
|
|
||||||
.layout-navList li a:visited {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-navUnderline {
|
|
||||||
height: 4px;
|
|
||||||
background-color: black;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
|
@ -1,14 +1,47 @@
|
||||||
require('./Layout.css');
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Switch, Route, Link, withRouter } from 'react-router-dom';
|
||||||
|
import { Motion, spring } from 'react-motion';
|
||||||
|
|
||||||
const React = require('react');
|
import WindowSize from './WindowSize';
|
||||||
const PropTypes = require('prop-types');
|
import About from './About';
|
||||||
const { Switch, Route, Link, withRouter } = require('react-router-dom');
|
import Stats from './Stats';
|
||||||
const { Motion, spring } = require('react-motion');
|
import Home from './Home';
|
||||||
|
|
||||||
const WindowSize = require('./WindowSize');
|
const styles = {
|
||||||
const About = require('./About');
|
title: {
|
||||||
const Stats = require('./Stats');
|
margin: 0,
|
||||||
const Home = require('./Home');
|
textTransform: 'uppercase',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: '5em'
|
||||||
|
},
|
||||||
|
nav: {
|
||||||
|
margin: '0 0 3em'
|
||||||
|
},
|
||||||
|
navList: {
|
||||||
|
margin: 0,
|
||||||
|
padding: 0,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
navListItem: {
|
||||||
|
flexBasis: 'auto',
|
||||||
|
listStyleType: 'none',
|
||||||
|
display: 'inline-block',
|
||||||
|
fontSize: '1.1em',
|
||||||
|
margin: '0 10px'
|
||||||
|
},
|
||||||
|
navLink: {
|
||||||
|
textDecoration: 'none',
|
||||||
|
color: 'black'
|
||||||
|
},
|
||||||
|
navUnderline: {
|
||||||
|
height: 4,
|
||||||
|
backgroundColor: 'black',
|
||||||
|
position: 'absolute',
|
||||||
|
left: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class Layout extends React.Component {
|
class Layout extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -66,8 +99,9 @@ class Layout extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (prevProps.location.pathname !== this.props.location.pathname)
|
if (prevProps.location.pathname !== this.props.location.pathname) {
|
||||||
this.adjustUnderline(true);
|
this.adjustUnderline(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -85,20 +119,23 @@ class Layout extends React.Component {
|
||||||
<WindowSize onChange={this.adjustUnderline} />
|
<WindowSize onChange={this.adjustUnderline} />
|
||||||
<div className="wrapper">
|
<div className="wrapper">
|
||||||
<header>
|
<header>
|
||||||
<h1 className="layout-title">unpkg</h1>
|
<h1 style={styles.title}>unpkg</h1>
|
||||||
<nav className="layout-nav">
|
<nav style={styles.nav}>
|
||||||
<ol
|
<ol style={styles.navList} ref={node => (this.listNode = node)}>
|
||||||
className="layout-navList"
|
<li style={styles.navListItem}>
|
||||||
ref={node => (this.listNode = node)}
|
<Link to="/" style={styles.navLink}>
|
||||||
>
|
Home
|
||||||
<li>
|
</Link>
|
||||||
<Link to="/">Home</Link>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li style={styles.navListItem}>
|
||||||
<Link to="/stats">Stats</Link>
|
<Link to="/stats" style={styles.navLink}>
|
||||||
|
Stats
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li style={styles.navListItem}>
|
||||||
<Link to="/about">About</Link>
|
<Link to="/about" style={styles.navLink}>
|
||||||
|
About
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
<Motion
|
<Motion
|
||||||
|
@ -106,8 +143,8 @@ class Layout extends React.Component {
|
||||||
style={style}
|
style={style}
|
||||||
children={style => (
|
children={style => (
|
||||||
<div
|
<div
|
||||||
className="layout-navUnderline"
|
|
||||||
style={{
|
style={{
|
||||||
|
...styles.navUnderline,
|
||||||
WebkitTransform: `translate3d(${style.left}px,0,0)`,
|
WebkitTransform: `translate3d(${style.left}px,0,0)`,
|
||||||
transform: `translate3d(${style.left}px,0,0)`,
|
transform: `translate3d(${style.left}px,0,0)`,
|
||||||
width: style.width
|
width: style.width
|
||||||
|
@ -132,4 +169,4 @@ class Layout extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = withRouter(Layout);
|
export default withRouter(Layout);
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
.table-filter {
|
|
||||||
font-size: 0.8em;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.regions-table .country-row td.country-name {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
|
@ -1,14 +1,24 @@
|
||||||
require('./Stats.css');
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import formatBytes from 'pretty-bytes';
|
||||||
|
import formatDate from 'date-fns/format';
|
||||||
|
import parseDate from 'date-fns/parse';
|
||||||
|
|
||||||
const React = require('react');
|
import { continents, countries } from './countries.json';
|
||||||
const PropTypes = require('prop-types');
|
|
||||||
const formatBytes = require('pretty-bytes');
|
|
||||||
const formatDate = require('date-fns/format');
|
|
||||||
const parseDate = require('date-fns/parse');
|
|
||||||
const { continents, countries } = require('countries-list');
|
|
||||||
|
|
||||||
const formatNumber = require('../utils/formatNumber');
|
import Wrapper from './Wrapper';
|
||||||
const formatPercent = require('../utils/formatPercent');
|
import formatNumber from '../utils/formatNumber';
|
||||||
|
import formatPercent from '../utils/formatPercent';
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
tableFilter: {
|
||||||
|
fontSize: '0.8em',
|
||||||
|
textAlign: 'right'
|
||||||
|
},
|
||||||
|
countryName: {
|
||||||
|
paddingLeft: 20
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function getCountriesByContinent(continent) {
|
function getCountriesByContinent(continent) {
|
||||||
return Object.keys(countries).filter(
|
return Object.keys(countries).filter(
|
||||||
|
@ -20,17 +30,17 @@ function sumKeyValues(hash, keys) {
|
||||||
return keys.reduce((n, key) => n + (hash[key] || 0), 0);
|
return keys.reduce((n, key) => n + (hash[key] || 0), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sumValues(hash) {
|
// function sumValues(hash) {
|
||||||
return Object.keys(hash).reduce((memo, key) => memo + hash[key], 0);
|
// return Object.keys(hash).reduce((memo, key) => memo + hash[key], 0);
|
||||||
}
|
// }
|
||||||
|
|
||||||
class Stats extends React.Component {
|
export default class Stats extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
data: PropTypes.object
|
data: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
minPackageRequests: 1000000,
|
// minPackageRequests: 1000000,
|
||||||
minCountryRequests: 1000000
|
minCountryRequests: 1000000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,45 +56,45 @@ class Stats extends React.Component {
|
||||||
const until = parseDate(totals.until);
|
const until = parseDate(totals.until);
|
||||||
|
|
||||||
// Packages
|
// Packages
|
||||||
const packageRows = [];
|
// const packageRows = [];
|
||||||
|
|
||||||
Object.keys(totals.requests.package)
|
// Object.keys(totals.requests.package)
|
||||||
.sort((a, b) => {
|
// .sort((a, b) => {
|
||||||
return totals.requests.package[b] - totals.requests.package[a];
|
// return totals.requests.package[b] - totals.requests.package[a];
|
||||||
})
|
// })
|
||||||
.forEach(packageName => {
|
// .forEach(packageName => {
|
||||||
const requests = totals.requests.package[packageName];
|
// const requests = totals.requests.package[packageName];
|
||||||
const bandwidth = totals.bandwidth.package[packageName];
|
// const bandwidth = totals.bandwidth.package[packageName];
|
||||||
|
|
||||||
if (requests >= this.state.minPackageRequests) {
|
// if (requests >= this.state.minPackageRequests) {
|
||||||
packageRows.push(
|
// packageRows.push(
|
||||||
<tr key={packageName}>
|
// <tr key={packageName}>
|
||||||
<td>
|
// <td>
|
||||||
<a
|
// <a
|
||||||
href={`https://npmjs.org/package/${packageName}`}
|
// href={`https://npmjs.org/package/${packageName}`}
|
||||||
title={`${packageName} on npm`}
|
// title={`${packageName} on npm`}
|
||||||
>
|
// >
|
||||||
{packageName}
|
// {packageName}
|
||||||
</a>
|
// </a>
|
||||||
</td>
|
// </td>
|
||||||
<td>
|
// <td>
|
||||||
{formatNumber(requests)} (
|
// {formatNumber(requests)} (
|
||||||
{formatPercent(requests / totals.requests.all)}
|
// {formatPercent(requests / totals.requests.all)}
|
||||||
%)
|
// %)
|
||||||
</td>
|
// </td>
|
||||||
{bandwidth ? (
|
// {bandwidth ? (
|
||||||
<td>
|
// <td>
|
||||||
{formatBytes(bandwidth)} (
|
// {formatBytes(bandwidth)} (
|
||||||
{formatPercent(bandwidth / totals.bandwidth.all)}
|
// {formatPercent(bandwidth / totals.bandwidth.all)}
|
||||||
%)
|
// %)
|
||||||
</td>
|
// </td>
|
||||||
) : (
|
// ) : (
|
||||||
<td>-</td>
|
// <td>-</td>
|
||||||
)}
|
// )}
|
||||||
</tr>
|
// </tr>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
// Regions
|
// Regions
|
||||||
const regionRows = [];
|
const regionRows = [];
|
||||||
|
@ -114,7 +124,7 @@ class Stats extends React.Component {
|
||||||
continentData.bandwidth !== 0
|
continentData.bandwidth !== 0
|
||||||
) {
|
) {
|
||||||
regionRows.push(
|
regionRows.push(
|
||||||
<tr key={continent} className="continent-row">
|
<tr key={continent}>
|
||||||
<td>
|
<td>
|
||||||
<strong>{continentName}</strong>
|
<strong>{continentName}</strong>
|
||||||
</td>
|
</td>
|
||||||
|
@ -145,8 +155,8 @@ class Stats extends React.Component {
|
||||||
|
|
||||||
if (countryRequests > this.state.minCountryRequests) {
|
if (countryRequests > this.state.minCountryRequests) {
|
||||||
regionRows.push(
|
regionRows.push(
|
||||||
<tr key={continent + country} className="country-row">
|
<tr key={continent + country}>
|
||||||
<td className="country-name">{countries[country].name}</td>
|
<td style={styles.countryName}>{countries[country].name}</td>
|
||||||
<td>
|
<td>
|
||||||
{formatNumber(countryRequests)} (
|
{formatNumber(countryRequests)} (
|
||||||
{formatPercent(countryRequests / totals.requests.all)}
|
{formatPercent(countryRequests / totals.requests.all)}
|
||||||
|
@ -165,27 +175,27 @@ class Stats extends React.Component {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Protocols
|
// Protocols
|
||||||
const protocolRows = Object.keys(totals.requests.protocol)
|
// const protocolRows = Object.keys(totals.requests.protocol)
|
||||||
.sort((a, b) => {
|
// .sort((a, b) => {
|
||||||
return totals.requests.protocol[b] - totals.requests.protocol[a];
|
// return totals.requests.protocol[b] - totals.requests.protocol[a];
|
||||||
})
|
// })
|
||||||
.map(protocol => {
|
// .map(protocol => {
|
||||||
const requests = totals.requests.protocol[protocol];
|
// const requests = totals.requests.protocol[protocol];
|
||||||
|
|
||||||
return (
|
// return (
|
||||||
<tr key={protocol}>
|
// <tr key={protocol}>
|
||||||
<td>{protocol}</td>
|
// <td>{protocol}</td>
|
||||||
<td>
|
// <td>
|
||||||
{formatNumber(requests)} (
|
// {formatNumber(requests)} (
|
||||||
{formatPercent(requests / sumValues(totals.requests.protocol))}
|
// {formatPercent(requests / sumValues(totals.requests.protocol))}
|
||||||
%)
|
// %)
|
||||||
</td>
|
// </td>
|
||||||
</tr>
|
// </tr>
|
||||||
);
|
// );
|
||||||
});
|
// });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="wrapper">
|
<Wrapper>
|
||||||
<p>
|
<p>
|
||||||
From <strong>{formatDate(since, 'MMM D')}</strong> to{' '}
|
From <strong>{formatDate(since, 'MMM D')}</strong> to{' '}
|
||||||
<strong>{formatDate(until, 'MMM D')}</strong> unpkg served{' '}
|
<strong>{formatDate(until, 'MMM D')}</strong> unpkg served{' '}
|
||||||
|
@ -201,6 +211,12 @@ class Stats extends React.Component {
|
||||||
|
|
||||||
<h3>Packages</h3>
|
<h3>Packages</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
We recently migrated unpkg to a new backend and are working on getting
|
||||||
|
package-specific data back on the site.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/*
|
||||||
<p>
|
<p>
|
||||||
The table below shows the most popular packages served by unpkg from{' '}
|
The table below shows the most popular packages served by unpkg from{' '}
|
||||||
<strong>{formatDate(since, 'MMM D')}</strong> to{' '}
|
<strong>{formatDate(since, 'MMM D')}</strong> to{' '}
|
||||||
|
@ -208,7 +224,7 @@ class Stats extends React.Component {
|
||||||
{Object.keys(totals.requests.package).length} packages are shown.
|
{Object.keys(totals.requests.package).length} packages are shown.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="table-filter">
|
<p style={styles.tableFilter}>
|
||||||
Include only packages that received at least{' '}
|
Include only packages that received at least{' '}
|
||||||
<select
|
<select
|
||||||
value={this.state.minPackageRequests}
|
value={this.state.minPackageRequests}
|
||||||
|
@ -244,6 +260,7 @@ class Stats extends React.Component {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>{packageRows}</tbody>
|
<tbody>{packageRows}</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
*/}
|
||||||
|
|
||||||
<h3>Regions</h3>
|
<h3>Regions</h3>
|
||||||
|
|
||||||
|
@ -253,7 +270,7 @@ class Stats extends React.Component {
|
||||||
<strong>{formatDate(until, 'MMM D')}</strong> by geographic region.
|
<strong>{formatDate(until, 'MMM D')}</strong> by geographic region.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="table-filter">
|
<p style={styles.tableFilter}>
|
||||||
Include only countries that made at least{' '}
|
Include only countries that made at least{' '}
|
||||||
<select
|
<select
|
||||||
value={this.state.minCountryRequests}
|
value={this.state.minCountryRequests}
|
||||||
|
@ -272,12 +289,7 @@ class Stats extends React.Component {
|
||||||
requests.
|
requests.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<table
|
<table cellSpacing="0" cellPadding="0" style={{ width: '100%' }}>
|
||||||
cellSpacing="0"
|
|
||||||
cellPadding="0"
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
className="regions-table"
|
|
||||||
>
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
|
@ -294,6 +306,7 @@ class Stats extends React.Component {
|
||||||
<tbody>{regionRows}</tbody>
|
<tbody>{regionRows}</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
{/*
|
||||||
<h3>Protocols</h3>
|
<h3>Protocols</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -315,9 +328,8 @@ class Stats extends React.Component {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>{protocolRows}</tbody>
|
<tbody>{protocolRows}</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
*/}
|
||||||
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Stats;
|
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const PropTypes = require('prop-types');
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const addEvent = require('../utils/addEvent');
|
import { addEvent, removeEvent } from '../utils/dom';
|
||||||
const removeEvent = require('../utils/removeEvent');
|
|
||||||
|
|
||||||
const resizeEvent = 'resize';
|
const resizeEvent = 'resize';
|
||||||
|
|
||||||
class WindowSize extends React.Component {
|
export default class WindowSize extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onChange: PropTypes.func
|
onChange: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
handleWindowResize = () => {
|
handleWindowResize = () => {
|
||||||
if (this.props.onChange)
|
if (this.props.onChange) {
|
||||||
this.props.onChange({
|
this.props.onChange({
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
height: window.innerHeight
|
height: window.innerHeight
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -31,5 +31,3 @@ class WindowSize extends React.Component {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = WindowSize;
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function Wrapper({ children }) {
|
||||||
|
return <div style={{ maxWidth: 700, margin: '0 auto' }}>{children}</div>;
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -1,9 +0,0 @@
|
||||||
function addEvent(node, type, handler) {
|
|
||||||
if (node.addEventListener) {
|
|
||||||
node.addEventListener(type, handler, false);
|
|
||||||
} else if (node.attachEvent) {
|
|
||||||
node.attachEvent('on' + type, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = addEvent;
|
|
|
@ -1,5 +1,3 @@
|
||||||
function createHTML(code) {
|
export default function createHTML(code) {
|
||||||
return { __html: code };
|
return { __html: code };
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = createHTML;
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
export function addEvent(node, type, handler) {
|
||||||
|
if (node.addEventListener) {
|
||||||
|
node.addEventListener(type, handler, false);
|
||||||
|
} else if (node.attachEvent) {
|
||||||
|
node.attachEvent('on' + type, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeEvent(node, type, handler) {
|
||||||
|
if (node.removeEventListener) {
|
||||||
|
node.removeEventListener(type, handler, false);
|
||||||
|
} else if (node.detachEvent) {
|
||||||
|
node.detachEvent('on' + type, handler);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
|
|
||||||
const h = require('./createHTML');
|
import h from './createHTML';
|
||||||
|
|
||||||
function execScript(code) {
|
export default function execScript(code) {
|
||||||
return <script dangerouslySetInnerHTML={h(code)} />;
|
return <script dangerouslySetInnerHTML={h(code)} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = execScript;
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
function formatNumber(n) {
|
export default function formatNumber(n) {
|
||||||
const digits = String(n).split('');
|
const digits = String(n).split('');
|
||||||
const groups = [];
|
const groups = [];
|
||||||
|
|
||||||
|
@ -8,5 +8,3 @@ function formatNumber(n) {
|
||||||
|
|
||||||
return groups.join(',');
|
return groups.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = formatNumber;
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
function formatPercent(n, fixed = 1) {
|
export default function formatPercent(n, fixed = 1) {
|
||||||
return String((n.toPrecision(2) * 100).toFixed(fixed));
|
return String((n.toPrecision(2) * 100).toFixed(fixed));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = formatPercent;
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
function parseNumber(s) {
|
export default function parseNumber(s) {
|
||||||
return parseInt(s.replace(/,/g, ''), 10) || 0;
|
return parseInt(s.replace(/,/g, ''), 10) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = parseNumber;
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
function removeEvent(node, type, handler) {
|
|
||||||
if (node.removeEventListener) {
|
|
||||||
node.removeEventListener(type, handler, false);
|
|
||||||
} else if (node.detachEvent) {
|
|
||||||
node.detachEvent('on' + type, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = removeEvent;
|
|
|
@ -1,9 +0,0 @@
|
||||||
// Use babel to compile JSX on the fly.
|
|
||||||
require('@babel/register')({
|
|
||||||
only: [/modules\/client/]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ignore require("*.css") calls.
|
|
||||||
require.extensions['.css'] = function() {
|
|
||||||
return {};
|
|
||||||
};
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const npmRegistryURL =
|
||||||
|
process.env.NPM_REGISTRY_URL || 'https://registry.npmjs.org';
|
||||||
|
|
||||||
|
export const origin = process.env.ORIGIN || 'http://localhost:5000';
|
|
@ -1,42 +0,0 @@
|
||||||
const webpack = require('webpack');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a modified copy of the given webpackEntry object with
|
|
||||||
* the moduleId in front of all other assets.
|
|
||||||
*/
|
|
||||||
function prependModuleId(webpackEntry, moduleId) {
|
|
||||||
if (typeof webpackEntry === 'string') {
|
|
||||||
return [moduleId, webpackEntry];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(webpackEntry)) {
|
|
||||||
return [moduleId, ...webpackEntry];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (webpackEntry && typeof webpackEntry === 'object') {
|
|
||||||
const entry = { ...webpackEntry };
|
|
||||||
|
|
||||||
for (const chunkName in entry) {
|
|
||||||
if (entry.hasOwnProperty(chunkName)) {
|
|
||||||
entry[chunkName] = prependModuleId(entry[chunkName], moduleId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Invalid webpack entry object');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a webpack compiler that automatically inlines the
|
|
||||||
* webpack dev runtime in all entry points.
|
|
||||||
*/
|
|
||||||
function createDevCompiler(webpackConfig, webpackRuntimeModuleId) {
|
|
||||||
return webpack({
|
|
||||||
...webpackConfig,
|
|
||||||
entry: prependModuleId(webpackConfig.entry, webpackRuntimeModuleId)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = createDevCompiler;
|
|
|
@ -1,54 +0,0 @@
|
||||||
const express = require('express');
|
|
||||||
const morgan = require('morgan');
|
|
||||||
const WebpackDevServer = require('webpack-dev-server');
|
|
||||||
const devErrorHandler = require('errorhandler');
|
|
||||||
|
|
||||||
const devAssets = require('./middleware/devAssets');
|
|
||||||
const createDevCompiler = require('./createDevCompiler');
|
|
||||||
const createRouter = require('./createRouter');
|
|
||||||
|
|
||||||
function createDevServer(publicDir, webpackConfig, devOrigin) {
|
|
||||||
const compiler = createDevCompiler(
|
|
||||||
webpackConfig,
|
|
||||||
`webpack-dev-server/client?${devOrigin}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const server = new WebpackDevServer(compiler, {
|
|
||||||
// webpack-dev-middleware options
|
|
||||||
publicPath: webpackConfig.output.publicPath,
|
|
||||||
quiet: false,
|
|
||||||
noInfo: false,
|
|
||||||
stats: {
|
|
||||||
// https://webpack.js.org/configuration/stats/
|
|
||||||
assets: true,
|
|
||||||
colors: true,
|
|
||||||
version: true,
|
|
||||||
hash: true,
|
|
||||||
timings: true,
|
|
||||||
chunks: false
|
|
||||||
},
|
|
||||||
|
|
||||||
// webpack-dev-server options
|
|
||||||
contentBase: false,
|
|
||||||
disableHostCheck: true,
|
|
||||||
before(app) {
|
|
||||||
// This runs before webpack-dev-middleware
|
|
||||||
app.disable('x-powered-by');
|
|
||||||
app.use(morgan('dev'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// This runs after webpack-dev-middleware
|
|
||||||
server.use(devErrorHandler());
|
|
||||||
|
|
||||||
if (publicDir) {
|
|
||||||
server.use(express.static(publicDir));
|
|
||||||
}
|
|
||||||
|
|
||||||
server.use(devAssets(compiler));
|
|
||||||
server.use(createRouter());
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = createDevServer;
|
|
|
@ -1,103 +0,0 @@
|
||||||
const express = require('express');
|
|
||||||
const bodyParser = require('body-parser');
|
|
||||||
const cors = require('cors');
|
|
||||||
|
|
||||||
function route(setup) {
|
|
||||||
const app = express.Router();
|
|
||||||
setup(app);
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRouter() {
|
|
||||||
return route(app => {
|
|
||||||
app.get('/', require('./actions/serveRootPage'));
|
|
||||||
|
|
||||||
app.use(cors());
|
|
||||||
app.use(bodyParser.json());
|
|
||||||
app.use(require('./middleware/userToken'));
|
|
||||||
|
|
||||||
app.use(
|
|
||||||
'/api',
|
|
||||||
route(app => {
|
|
||||||
app.get('/publicKey', require('./actions/showPublicKey'));
|
|
||||||
|
|
||||||
app.post('/auth', require('./actions/createAuth'));
|
|
||||||
app.get('/auth', require('./actions/showAuth'));
|
|
||||||
|
|
||||||
app.post(
|
|
||||||
'/blacklist',
|
|
||||||
require('./middleware/requireAuth')('blacklist.add'),
|
|
||||||
require('./actions/addToBlacklist')
|
|
||||||
);
|
|
||||||
app.get(
|
|
||||||
'/blacklist',
|
|
||||||
require('./middleware/requireAuth')('blacklist.read'),
|
|
||||||
require('./actions/showBlacklist')
|
|
||||||
);
|
|
||||||
app.delete(
|
|
||||||
'/blacklist',
|
|
||||||
require('./middleware/requireAuth')('blacklist.remove'),
|
|
||||||
require('./actions/removeFromBlacklist')
|
|
||||||
);
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'test') {
|
|
||||||
app.get('/stats', require('./actions/showStats'));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: Remove
|
|
||||||
app.get('/_publicKey', require('./actions/showPublicKey'));
|
|
||||||
|
|
||||||
// TODO: Remove
|
|
||||||
app.use(
|
|
||||||
'/_auth',
|
|
||||||
route(app => {
|
|
||||||
app.post('/', require('./actions/createAuth'));
|
|
||||||
app.get('/', require('./actions/showAuth'));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: Remove
|
|
||||||
app.use(
|
|
||||||
'/_blacklist',
|
|
||||||
route(app => {
|
|
||||||
app.post(
|
|
||||||
'/',
|
|
||||||
require('./middleware/requireAuth')('blacklist.add'),
|
|
||||||
require('./actions/addToBlacklist')
|
|
||||||
);
|
|
||||||
app.get(
|
|
||||||
'/',
|
|
||||||
require('./middleware/requireAuth')('blacklist.read'),
|
|
||||||
require('./actions/showBlacklist')
|
|
||||||
);
|
|
||||||
app.delete(
|
|
||||||
'*',
|
|
||||||
require('./middleware/requireAuth')('blacklist.remove'),
|
|
||||||
require('./middleware/validatePackageURL'),
|
|
||||||
require('./actions/removeFromBlacklist')
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: Remove
|
|
||||||
if (process.env.NODE_ENV !== 'test') {
|
|
||||||
app.get('/_stats', require('./actions/showStats'));
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
'*',
|
|
||||||
require('./middleware/redirectLegacyURLs'),
|
|
||||||
require('./middleware/validatePackageURL'),
|
|
||||||
require('./middleware/validatePackageName'),
|
|
||||||
require('./middleware/validateQuery'),
|
|
||||||
require('./middleware/checkBlacklist'),
|
|
||||||
require('./middleware/fetchPackage'),
|
|
||||||
require('./middleware/findFile'),
|
|
||||||
require('./actions/serveFile')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = createRouter;
|
|
|
@ -1,93 +0,0 @@
|
||||||
const http = require('http');
|
|
||||||
const express = require('express');
|
|
||||||
const morgan = require('morgan');
|
|
||||||
const raven = require('raven');
|
|
||||||
|
|
||||||
const staticAssets = require('./middleware/staticAssets');
|
|
||||||
const createRouter = require('./createRouter');
|
|
||||||
|
|
||||||
morgan.token('fwd', req => {
|
|
||||||
const fwd = req.get('x-forwarded-for');
|
|
||||||
return fwd ? fwd.replace(/\s/g, '') : '-';
|
|
||||||
});
|
|
||||||
|
|
||||||
if (process.env.SENTRY_DSN) {
|
|
||||||
raven
|
|
||||||
.config(process.env.SENTRY_DSN, {
|
|
||||||
release: process.env.HEROKU_RELEASE_VERSION,
|
|
||||||
autoBreadcrumbs: true
|
|
||||||
})
|
|
||||||
.install();
|
|
||||||
}
|
|
||||||
|
|
||||||
// function errorHandler(err, req, res, next) {
|
|
||||||
// console.error(err.stack);
|
|
||||||
|
|
||||||
// res
|
|
||||||
// .status(500)
|
|
||||||
// .type("text")
|
|
||||||
// .send("Internal Server Error");
|
|
||||||
|
|
||||||
// next(err);
|
|
||||||
// }
|
|
||||||
|
|
||||||
function createServer(publicDir, statsFile) {
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
app.disable('x-powered-by');
|
|
||||||
|
|
||||||
if (process.env.SENTRY_DSN) {
|
|
||||||
app.use(raven.requestHandler());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'test') {
|
|
||||||
app.use(
|
|
||||||
morgan(
|
|
||||||
// Modified version of Heroku's log format
|
|
||||||
// https://devcenter.heroku.com/articles/http-routing#heroku-router-log-format
|
|
||||||
'method=:method path=":url" host=:req[host] request_id=:req[x-request-id] cf_ray=:req[cf-ray] fwd=:fwd status=:status bytes=:res[content-length]'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// app.use(errorHandler);
|
|
||||||
|
|
||||||
if (publicDir) {
|
|
||||||
app.use(express.static(publicDir, { maxAge: '365d' }));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statsFile) {
|
|
||||||
app.use(staticAssets(statsFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
app.use(createRouter());
|
|
||||||
|
|
||||||
if (process.env.SENTRY_DSN) {
|
|
||||||
app.use(raven.errorHandler());
|
|
||||||
}
|
|
||||||
|
|
||||||
const server = http.createServer(app);
|
|
||||||
|
|
||||||
// Heroku dynos automatically timeout after 30s. Set our
|
|
||||||
// own timeout here to force sockets to close before that.
|
|
||||||
// https://devcenter.heroku.com/articles/request-timeout
|
|
||||||
server.setTimeout(25000, socket => {
|
|
||||||
const message = `Timeout of 25 seconds exceeded`;
|
|
||||||
|
|
||||||
socket.end(
|
|
||||||
[
|
|
||||||
'HTTP/1.1 503 Service Unavailable',
|
|
||||||
'Date: ' + new Date().toGMTString(),
|
|
||||||
'Content-Length: ' + Buffer.byteLength(message),
|
|
||||||
'Content-Type: text/plain',
|
|
||||||
'Connection: close',
|
|
||||||
'',
|
|
||||||
message
|
|
||||||
].join('\r\n')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = createServer;
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { https } from 'firebase-functions';
|
||||||
|
|
||||||
|
// import serveAuth from './serveAuth';
|
||||||
|
import serveAutoIndexPage from './serveAutoIndexPage';
|
||||||
|
import serveNpmPackageFile from './serveNpmPackageFile';
|
||||||
|
import servePublicKey from './servePublicKey';
|
||||||
|
import serveStats from './serveStats';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// serveAuth: https.onRequest(serveAuth),
|
||||||
|
serveAutoIndexPage: https.onRequest(serveAutoIndexPage),
|
||||||
|
serveNpmPackageFile: https.onRequest(serveNpmPackageFile),
|
||||||
|
servePublicKey: https.onRequest(servePublicKey),
|
||||||
|
serveStats: https.onRequest(serveStats)
|
||||||
|
};
|
|
@ -1,14 +1,11 @@
|
||||||
const parseURL = require('url').parse;
|
import url from 'url';
|
||||||
const startOfDay = require('date-fns/start_of_day');
|
import { startOfDay, addDays } from 'date-fns';
|
||||||
const addDays = require('date-fns/add_days');
|
|
||||||
|
|
||||||
const db = require('./utils/data');
|
import data from '../utils/data';
|
||||||
const isValidPackageName = require('./utils/isValidPackageName');
|
import isValidPackageName from '../utils/isValidPackageName';
|
||||||
const parsePackageURL = require('./utils/parsePackageURL');
|
import parsePackageURL from '../utils/parsePackageURL';
|
||||||
const logging = require('./utils/logging');
|
import * as cloudflare from '../utils/cloudflare';
|
||||||
|
import * as stats from '../utils/stats';
|
||||||
const CloudflareAPI = require('./CloudflareAPI');
|
|
||||||
const StatsAPI = require('./StatsAPI');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Domains we want to analyze.
|
* Domains we want to analyze.
|
||||||
|
@ -54,13 +51,13 @@ function computeCounters(stream) {
|
||||||
const nextDay = startOfDay(addDays(date, 1));
|
const nextDay = startOfDay(addDays(date, 1));
|
||||||
const sevenDaysLater = getSeconds(addDays(nextDay, 7));
|
const sevenDaysLater = getSeconds(addDays(nextDay, 7));
|
||||||
const thirtyDaysLater = getSeconds(addDays(nextDay, 30));
|
const thirtyDaysLater = getSeconds(addDays(nextDay, 30));
|
||||||
const dayKey = StatsAPI.createDayKey(date);
|
const dayKey = stats.createDayKey(date);
|
||||||
|
|
||||||
if (entry.EdgeResponseStatus === 200) {
|
if (entry.EdgeResponseStatus === 200) {
|
||||||
// Q: How many requests do we serve for a package per day?
|
// Q: How many requests do we serve for a package per day?
|
||||||
// Q: How many bytes do we serve for a package per day?
|
// Q: How many bytes do we serve for a package per day?
|
||||||
const url = parsePackageURL(entry.ClientRequestURI);
|
const parsed = parsePackageURL(entry.ClientRequestURI);
|
||||||
const packageName = url && url.packageName;
|
const packageName = parsed && parsed.packageName;
|
||||||
|
|
||||||
if (packageName && isValidPackageName(packageName)) {
|
if (packageName && isValidPackageName(packageName)) {
|
||||||
incr(
|
incr(
|
||||||
|
@ -93,7 +90,7 @@ function computeCounters(stream) {
|
||||||
// Q: How many requests do we receive from a hostname per day?
|
// Q: How many requests do we receive from a hostname per day?
|
||||||
// Q: How many bytes do we serve to a hostname per day?
|
// Q: How many bytes do we serve to a hostname per day?
|
||||||
const referer = entry.ClientRequestReferer;
|
const referer = entry.ClientRequestReferer;
|
||||||
const hostname = referer && parseURL(referer).hostname;
|
const hostname = referer && url.parse(referer).hostname;
|
||||||
|
|
||||||
if (hostname) {
|
if (hostname) {
|
||||||
incr(`stats-hostnameRequests-${dayKey}`, hostname, 1, sevenDaysLater);
|
incr(`stats-hostnameRequests-${dayKey}`, hostname, 1, sevenDaysLater);
|
||||||
|
@ -118,11 +115,11 @@ function processLogs(stream) {
|
||||||
const values = counters[key];
|
const values = counters[key];
|
||||||
|
|
||||||
Object.keys(values).forEach(member => {
|
Object.keys(values).forEach(member => {
|
||||||
db.zincrby(key, values[member], member);
|
data.zincrby(key, values[member], member);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (expireat[key]) {
|
if (expireat[key]) {
|
||||||
db.expireat(key, expireat[key]);
|
data.expireat(key, expireat[key]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -145,44 +142,46 @@ function ingestLogsForZone(zone, startDate, endDate) {
|
||||||
'ClientRequestReferer'
|
'ClientRequestReferer'
|
||||||
];
|
];
|
||||||
|
|
||||||
return CloudflareAPI.getLogs(
|
return cloudflare
|
||||||
zone.id,
|
.getLogs(
|
||||||
stringifySeconds(startSeconds),
|
zone.id,
|
||||||
stringifySeconds(endSeconds),
|
|
||||||
fields
|
|
||||||
).then(stream => {
|
|
||||||
const endFetchTime = Date.now();
|
|
||||||
|
|
||||||
logging.info(
|
|
||||||
'Fetched logs for %s from %s to %s (%dms)',
|
|
||||||
zone.name,
|
|
||||||
stringifySeconds(startSeconds),
|
stringifySeconds(startSeconds),
|
||||||
stringifySeconds(endSeconds),
|
stringifySeconds(endSeconds),
|
||||||
endFetchTime - startFetchTime
|
fields
|
||||||
);
|
)
|
||||||
|
.then(stream => {
|
||||||
|
const endFetchTime = Date.now();
|
||||||
|
|
||||||
const startProcessTime = Date.now();
|
console.log(
|
||||||
|
'Fetched logs for %s from %s to %s (%dms)',
|
||||||
return processLogs(stream).then(totalEntries => {
|
|
||||||
const endProcessTime = Date.now();
|
|
||||||
|
|
||||||
logging.info(
|
|
||||||
'Processed %d log entries for %s (%dms)',
|
|
||||||
totalEntries,
|
|
||||||
zone.name,
|
zone.name,
|
||||||
endProcessTime - startProcessTime
|
stringifySeconds(startSeconds),
|
||||||
|
stringifySeconds(endSeconds),
|
||||||
|
endFetchTime - startFetchTime
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const startProcessTime = Date.now();
|
||||||
|
|
||||||
|
return processLogs(stream).then(totalEntries => {
|
||||||
|
const endProcessTime = Date.now();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'Processed %d log entries for %s (%dms)',
|
||||||
|
totalEntries,
|
||||||
|
zone.name,
|
||||||
|
endProcessTime - startProcessTime
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getZones(domainNames) {
|
function getZones(domainNames) {
|
||||||
return Promise.all(domainNames.map(CloudflareAPI.getZones)).then(results =>
|
return Promise.all(domainNames.map(cloudflare.getZones)).then(results =>
|
||||||
results.reduce((memo, zones) => memo.concat(zones))
|
results.reduce((memo, zones) => memo.concat(zones))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ingestLogs(startDate, endDate) {
|
export default function ingestLogs(startDate, endDate) {
|
||||||
return Promise.resolve(cachedZones || getZones(domainNames)).then(zones => {
|
return Promise.resolve(cachedZones || getZones(domainNames)).then(zones => {
|
||||||
if (!cachedZones) cachedZones = zones;
|
if (!cachedZones) cachedZones = zones;
|
||||||
|
|
||||||
|
@ -191,5 +190,3 @@ function ingestLogs(startDate, endDate) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ingestLogs;
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
import userToken from '../middleware/userToken';
|
||||||
|
import showAuth from '../actions/showAuth';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.disable('x-powered-by');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(userToken);
|
||||||
|
app.use(showAuth);
|
||||||
|
|
||||||
|
export default app;
|
|
@ -0,0 +1,27 @@
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
// import checkBlacklist from '../middleware/checkBlacklist';
|
||||||
|
import fetchPackage from '../middleware/fetchPackage';
|
||||||
|
import findFile from '../middleware/findFile';
|
||||||
|
import redirectLegacyURLs from '../middleware/redirectLegacyURLs';
|
||||||
|
import validatePackageURL from '../middleware/validatePackageURL';
|
||||||
|
import validatePackageName from '../middleware/validatePackageName';
|
||||||
|
import validateQuery from '../middleware/validateQuery';
|
||||||
|
import serveAutoIndexPage from '../actions/serveAutoIndexPage';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.disable('x-powered-by');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(redirectLegacyURLs);
|
||||||
|
app.use(validatePackageURL);
|
||||||
|
app.use(validatePackageName);
|
||||||
|
app.use(validateQuery);
|
||||||
|
// app.use(checkBlacklist);
|
||||||
|
app.use(fetchPackage);
|
||||||
|
app.use(findFile);
|
||||||
|
app.use(serveAutoIndexPage);
|
||||||
|
|
||||||
|
export default app;
|
|
@ -0,0 +1,27 @@
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
// import checkBlacklist from '../middleware/checkBlacklist';
|
||||||
|
import fetchPackage from '../middleware/fetchPackage';
|
||||||
|
import findFile from '../middleware/findFile';
|
||||||
|
import redirectLegacyURLs from '../middleware/redirectLegacyURLs';
|
||||||
|
import validatePackageURL from '../middleware/validatePackageURL';
|
||||||
|
import validatePackageName from '../middleware/validatePackageName';
|
||||||
|
import validateQuery from '../middleware/validateQuery';
|
||||||
|
import serveFile from '../actions/serveFile';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.disable('x-powered-by');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(redirectLegacyURLs);
|
||||||
|
app.use(validatePackageURL);
|
||||||
|
app.use(validatePackageName);
|
||||||
|
app.use(validateQuery);
|
||||||
|
// app.use(checkBlacklist);
|
||||||
|
app.use(fetchPackage);
|
||||||
|
app.use(findFile);
|
||||||
|
app.use(serveFile);
|
||||||
|
|
||||||
|
export default app;
|
|
@ -0,0 +1,13 @@
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
import showPublicKey from '../actions/showPublicKey';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.disable('x-powered-by');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(showPublicKey);
|
||||||
|
|
||||||
|
export default app;
|
|
@ -0,0 +1,13 @@
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
import showStats from '../actions/showStats';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.disable('x-powered-by');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(showStats);
|
||||||
|
|
||||||
|
export default app;
|
|
@ -1,43 +0,0 @@
|
||||||
const addMinutes = require('date-fns/add_minutes');
|
|
||||||
const startOfMinute = require('date-fns/start_of_minute');
|
|
||||||
|
|
||||||
const ingestLogs = require('./ingestLogs');
|
|
||||||
|
|
||||||
const oneSecond = 1000;
|
|
||||||
const oneMinute = oneSecond * 60;
|
|
||||||
|
|
||||||
let currentWorkload, timer;
|
|
||||||
|
|
||||||
function work() {
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
// The log for a request is typically available within thirty (30) minutes
|
|
||||||
// of the request taking place under normal conditions. We deliver logs
|
|
||||||
// ordered by the time that the logs were created, i.e. the timestamp of
|
|
||||||
// the request when it was received by the edge. Given the order of
|
|
||||||
// delivery, we recommend waiting a full thirty minutes to ingest a full
|
|
||||||
// set of logs. This will help ensure that any congestion in the log
|
|
||||||
// pipeline has passed and a full set of logs can be ingested.
|
|
||||||
// https://support.cloudflare.com/hc/en-us/articles/216672448-Enterprise-Log-Share-REST-API
|
|
||||||
const start = startOfMinute(now - oneMinute * 31);
|
|
||||||
const end = addMinutes(start, 1);
|
|
||||||
|
|
||||||
currentWorkload = ingestLogs(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
function shutdown() {
|
|
||||||
console.log('Shutting down...');
|
|
||||||
|
|
||||||
clearInterval(timer);
|
|
||||||
|
|
||||||
currentWorkload.then(() => {
|
|
||||||
console.log('Goodbye!');
|
|
||||||
process.exit();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
work();
|
|
||||||
|
|
||||||
process.on('SIGINT', shutdown).on('SIGTERM', shutdown);
|
|
||||||
|
|
||||||
timer = setInterval(work, oneMinute);
|
|
|
@ -1,7 +1,7 @@
|
||||||
const BlacklistAPI = require('../BlacklistAPI');
|
import { includesPackage } from '../utils/blacklist';
|
||||||
|
|
||||||
function checkBlacklist(req, res, next) {
|
export default function checkBlacklist(req, res, next) {
|
||||||
BlacklistAPI.includesPackage(req.packageName).then(
|
includesPackage(req.packageName).then(
|
||||||
blacklisted => {
|
blacklisted => {
|
||||||
// Disallow packages that have been blacklisted.
|
// Disallow packages that have been blacklisted.
|
||||||
if (blacklisted) {
|
if (blacklisted) {
|
||||||
|
@ -21,5 +21,3 @@ function checkBlacklist(req, res, next) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = checkBlacklist;
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
const invariant = require('invariant');
|
|
||||||
|
|
||||||
const createAssets = require('./utils/createAssets');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An express middleware that sets req.assets from the
|
|
||||||
* latest result from a running webpack compiler (i.e. using
|
|
||||||
* webpack-dev-middleware). Should only be used in dev.
|
|
||||||
*/
|
|
||||||
function devAssets(webpackCompiler) {
|
|
||||||
let assets;
|
|
||||||
webpackCompiler.plugin('done', stats => {
|
|
||||||
assets = createAssets(stats.toJson());
|
|
||||||
});
|
|
||||||
|
|
||||||
return (req, res, next) => {
|
|
||||||
invariant(
|
|
||||||
assets != null,
|
|
||||||
'devAssets middleware needs a running compiler; ' +
|
|
||||||
'use webpack-dev-middleware in front of devAssets'
|
|
||||||
);
|
|
||||||
|
|
||||||
req.assets = assets;
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = devAssets;
|
|
|
@ -1,10 +1,10 @@
|
||||||
const semver = require('semver');
|
import semver from 'semver';
|
||||||
|
|
||||||
const addLeadingSlash = require('../utils/addLeadingSlash');
|
import addLeadingSlash from '../utils/addLeadingSlash';
|
||||||
const createPackageURL = require('../utils/createPackageURL');
|
import createPackageURL from '../utils/createPackageURL';
|
||||||
const createSearch = require('../utils/createSearch');
|
import createSearch from '../utils/createSearch';
|
||||||
const getNpmPackageInfo = require('../utils/getNpmPackageInfo');
|
import getNpmPackageInfo from '../utils/getNpmPackageInfo';
|
||||||
const incrementCounter = require('../utils/incrementCounter');
|
// import incrementCounter from '../utils/incrementCounter';
|
||||||
|
|
||||||
function tagRedirect(req, res) {
|
function tagRedirect(req, res) {
|
||||||
const version = req.packageInfo['dist-tags'][req.packageVersion];
|
const version = req.packageInfo['dist-tags'][req.packageVersion];
|
||||||
|
@ -64,11 +64,11 @@ function filenameRedirect(req, res) {
|
||||||
|
|
||||||
// Count which packages are using this so we can warn them when we
|
// Count which packages are using this so we can warn them when we
|
||||||
// remove this functionality.
|
// remove this functionality.
|
||||||
incrementCounter(
|
// incrementCounter(
|
||||||
'package-json-custom-main',
|
// 'package-json-custom-main',
|
||||||
req.packageSpec + '?main=' + req.query.main,
|
// req.packageSpec + '?main=' + req.query.main,
|
||||||
1
|
// 1
|
||||||
);
|
// );
|
||||||
} else if (
|
} else if (
|
||||||
req.packageConfig.unpkg &&
|
req.packageConfig.unpkg &&
|
||||||
typeof req.packageConfig.unpkg === 'string'
|
typeof req.packageConfig.unpkg === 'string'
|
||||||
|
@ -83,7 +83,7 @@ function filenameRedirect(req, res) {
|
||||||
|
|
||||||
// Count which packages are using this so we can warn them when we
|
// Count which packages are using this so we can warn them when we
|
||||||
// remove this functionality.
|
// remove this functionality.
|
||||||
incrementCounter('package-json-browser-fallback', req.packageSpec, 1);
|
// incrementCounter('package-json-browser-fallback', req.packageSpec, 1);
|
||||||
} else {
|
} else {
|
||||||
filename = req.packageConfig.main || '/index.js';
|
filename = req.packageConfig.main || '/index.js';
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ function filenameRedirect(req, res) {
|
||||||
* version if the request targets a tag or uses a semver version, or to the
|
* version if the request targets a tag or uses a semver version, or to the
|
||||||
* exact filename if the request omits the filename.
|
* exact filename if the request omits the filename.
|
||||||
*/
|
*/
|
||||||
function fetchPackage(req, res, next) {
|
export default function fetchPackage(req, res, next) {
|
||||||
getNpmPackageInfo(req.packageName).then(
|
getNpmPackageInfo(req.packageName).then(
|
||||||
packageInfo => {
|
packageInfo => {
|
||||||
if (packageInfo == null || packageInfo.versions == null) {
|
if (packageInfo == null || packageInfo.versions == null) {
|
||||||
|
@ -149,5 +149,3 @@ function fetchPackage(req, res, next) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = fetchPackage;
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
|
|
||||||
const addLeadingSlash = require('../utils/addLeadingSlash');
|
import addLeadingSlash from '../utils/addLeadingSlash';
|
||||||
const createPackageURL = require('../utils/createPackageURL');
|
import createPackageURL from '../utils/createPackageURL';
|
||||||
const createSearch = require('../utils/createSearch');
|
import createSearch from '../utils/createSearch';
|
||||||
const fetchNpmPackage = require('../utils/fetchNpmPackage');
|
import fetchNpmPackage from '../utils/fetchNpmPackage';
|
||||||
const getIntegrity = require('../utils/getIntegrity');
|
import getIntegrity from '../utils/getIntegrity';
|
||||||
const getContentType = require('../utils/getContentType');
|
import getContentType from '../utils/getContentType';
|
||||||
|
|
||||||
function indexRedirect(req, res, entry) {
|
function indexRedirect(req, res, entry) {
|
||||||
// Redirect to the index file so relative imports
|
// Redirect to the index file so relative imports
|
||||||
|
@ -124,7 +124,7 @@ const multipleSlash = /\/\/+/;
|
||||||
* Fetch and search the archive to try and find the requested file.
|
* Fetch and search the archive to try and find the requested file.
|
||||||
* Redirect to the "index" file if a directory was requested.
|
* Redirect to the "index" file if a directory was requested.
|
||||||
*/
|
*/
|
||||||
function findFile(req, res, next) {
|
export default function findFile(req, res, next) {
|
||||||
fetchNpmPackage(req.packageConfig).then(tarballStream => {
|
fetchNpmPackage(req.packageConfig).then(tarballStream => {
|
||||||
const entryName = req.filename
|
const entryName = req.filename
|
||||||
.replace(multipleSlash, '/')
|
.replace(multipleSlash, '/')
|
||||||
|
@ -173,5 +173,3 @@ function findFile(req, res, next) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = findFile;
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const createSearch = require('../utils/createSearch');
|
import createSearch from '../utils/createSearch';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirect old URLs that we no longer support.
|
* Redirect old URLs that we no longer support.
|
||||||
*/
|
*/
|
||||||
function redirectLegacyURLs(req, res, next) {
|
export default function redirectLegacyURLs(req, res, next) {
|
||||||
// Permanently redirect /_meta/path to /path?meta.
|
// Permanently redirect /_meta/path to /path?meta.
|
||||||
if (req.path.match(/^\/_meta\//)) {
|
if (req.path.match(/^\/_meta\//)) {
|
||||||
req.query.meta = '';
|
req.query.meta = '';
|
||||||
|
@ -19,5 +19,3 @@ function redirectLegacyURLs(req, res, next) {
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = redirectLegacyURLs;
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Adds the given scope to the array in req.auth if the user has sufficient
|
* Adds the given scope to the array in req.auth if the user has sufficient
|
||||||
* permissions. Otherwise rejects the request.
|
* permissions. Otherwise rejects the request.
|
||||||
*/
|
*/
|
||||||
function requireAuth(scope) {
|
export default function requireAuth(scope) {
|
||||||
let checkScopes;
|
let checkScopes;
|
||||||
if (scope.includes('.')) {
|
if (scope.includes('.')) {
|
||||||
const parts = scope.split('.');
|
const parts = scope.split('.');
|
||||||
|
@ -36,5 +36,3 @@ function requireAuth(scope) {
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = requireAuth;
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
const fs = require('fs');
|
|
||||||
const invariant = require('invariant');
|
|
||||||
|
|
||||||
const createAssets = require('./utils/createAssets');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An express middleware that sets req.assets from the build
|
|
||||||
* info in the given stats file. Should be used in production.
|
|
||||||
*/
|
|
||||||
function staticAssets(webpackStatsFile) {
|
|
||||||
let stats;
|
|
||||||
try {
|
|
||||||
stats = JSON.parse(fs.readFileSync(webpackStatsFile, 'utf8'));
|
|
||||||
} catch (error) {
|
|
||||||
invariant(
|
|
||||||
false,
|
|
||||||
'staticAssets middleware cannot read the build stats in %s; ' +
|
|
||||||
'run the `build` script before starting the server',
|
|
||||||
webpackStatsFile
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const assets = createAssets(stats);
|
|
||||||
|
|
||||||
return (req, res, next) => {
|
|
||||||
req.assets = assets;
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = staticAssets;
|
|
|
@ -1,6 +1,4 @@
|
||||||
const AuthAPI = require('../AuthAPI');
|
import { verifyToken } from '../utils/auth';
|
||||||
|
|
||||||
const ReadMethods = { GET: true, HEAD: true };
|
|
||||||
|
|
||||||
function decodeBase64(string) {
|
function decodeBase64(string) {
|
||||||
return Buffer.from(string, 'base64').toString();
|
return Buffer.from(string, 'base64').toString();
|
||||||
|
@ -9,22 +7,20 @@ function decodeBase64(string) {
|
||||||
/**
|
/**
|
||||||
* Sets req.user from the payload in the auth token in the request.
|
* Sets req.user from the payload in the auth token in the request.
|
||||||
*/
|
*/
|
||||||
function userToken(req, res, next) {
|
export default function userToken(req, res, next) {
|
||||||
if (req.user) {
|
if (req.user !== undefined) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auth = req.get('Authorization');
|
const auth = req.get('Authorization');
|
||||||
const token = auth
|
const token = auth && decodeBase64(auth);
|
||||||
? decodeBase64(auth)
|
|
||||||
: (ReadMethods[req.method] ? req.query : req.body).token;
|
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
req.user = null;
|
req.user = null;
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthAPI.verifyToken(token).then(
|
verifyToken(token).then(
|
||||||
payload => {
|
payload => {
|
||||||
req.user = payload;
|
req.user = payload;
|
||||||
next();
|
next();
|
||||||
|
@ -44,5 +40,3 @@ function userToken(req, res, next) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = userToken;
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
/**
|
|
||||||
* Creates an assets object that is stored on req.assets.
|
|
||||||
*/
|
|
||||||
function createAssets(webpackStats) {
|
|
||||||
const { publicPath, assetsByChunkName } = webpackStats;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a public URL to the given asset.
|
|
||||||
*/
|
|
||||||
const createURL = asset => publicPath + asset;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of URLs to all assets in the given chunks.
|
|
||||||
*/
|
|
||||||
const getAll = (chunks = ['main']) =>
|
|
||||||
(Array.isArray(chunks) ? chunks : [chunks])
|
|
||||||
.reduce((memo, chunk) => memo.concat(assetsByChunkName[chunk] || []), [])
|
|
||||||
.map(createURL);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of URLs to all JavaScript files in the given chunks.
|
|
||||||
*/
|
|
||||||
const getScripts = (...chunks) =>
|
|
||||||
getAll(...chunks).filter(asset => /\.js$/.test(asset));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of URLs to all CSS files in the given chunks.
|
|
||||||
*/
|
|
||||||
const getStyles = (...chunks) =>
|
|
||||||
getAll(...chunks).filter(asset => /\.css$/.test(asset));
|
|
||||||
|
|
||||||
return {
|
|
||||||
createURL,
|
|
||||||
getAll,
|
|
||||||
getScripts,
|
|
||||||
getStyles
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = createAssets;
|
|
|
@ -1,4 +1,4 @@
|
||||||
const validateNpmPackageName = require('validate-npm-package-name');
|
import validateNpmPackageName from 'validate-npm-package-name';
|
||||||
|
|
||||||
const hexValue = /^[a-f0-9]+$/i;
|
const hexValue = /^[a-f0-9]+$/i;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ function isHash(value) {
|
||||||
/**
|
/**
|
||||||
* Reject requests for invalid npm package names.
|
* Reject requests for invalid npm package names.
|
||||||
*/
|
*/
|
||||||
function validatePackageName(req, res, next) {
|
export default function validatePackageName(req, res, next) {
|
||||||
if (isHash(req.packageName)) {
|
if (isHash(req.packageName)) {
|
||||||
return res
|
return res
|
||||||
.status(403)
|
.status(403)
|
||||||
|
@ -30,5 +30,3 @@ function validatePackageName(req, res, next) {
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = validatePackageName;
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
const parsePackageURL = require('../utils/parsePackageURL');
|
import parsePackageURL from '../utils/parsePackageURL';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the URL and add various properties to the request object to
|
* Parse the URL and add various properties to the request object to
|
||||||
* do with the package/file being requested. Reject invalid URLs.
|
* do with the package/file being requested. Reject invalid URLs.
|
||||||
*/
|
*/
|
||||||
function validatePackageURL(req, res, next) {
|
export default function validatePackageURL(req, res, next) {
|
||||||
const url = parsePackageURL(req.url);
|
const url = parsePackageURL(req.url);
|
||||||
|
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
|
@ -21,5 +21,3 @@ function validatePackageURL(req, res, next) {
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = validatePackageURL;
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const createSearch = require('../utils/createSearch');
|
import createSearch from '../utils/createSearch';
|
||||||
|
|
||||||
const knownQueryParams = {
|
const knownQueryParams = {
|
||||||
main: true, // Deprecated, see #63
|
main: true, // Deprecated, see #63
|
||||||
|
@ -23,12 +23,10 @@ function sanitizeQuery(originalQuery) {
|
||||||
/**
|
/**
|
||||||
* Reject URLs with invalid query parameters to increase cache hit rates.
|
* Reject URLs with invalid query parameters to increase cache hit rates.
|
||||||
*/
|
*/
|
||||||
function validateQuery(req, res, next) {
|
export default function validateQuery(req, res, next) {
|
||||||
if (!Object.keys(req.query).every(isKnownQueryParam)) {
|
if (!Object.keys(req.query).every(isKnownQueryParam)) {
|
||||||
return res.redirect(302, req.path + createSearch(sanitizeQuery(req.query)));
|
return res.redirect(302, req.path + createSearch(sanitizeQuery(req.query)));
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = validateQuery;
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const babel = require('babel-core');
|
import babel from 'babel-core';
|
||||||
const unpkgRewrite = require('../unpkgRewrite');
|
|
||||||
|
import unpkgRewrite from '../unpkgRewrite';
|
||||||
|
|
||||||
const testCases = [
|
const testCases = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const URL = require('whatwg-url');
|
import URL from 'whatwg-url';
|
||||||
const warning = require('warning');
|
import warning from 'warning';
|
||||||
|
|
||||||
const origin = require('../serverConfig').origin;
|
import { origin } from '../config';
|
||||||
|
|
||||||
const bareIdentifierFormat = /^((?:@[^/]+\/)?[^/]+)(\/.*)?$/;
|
const bareIdentifierFormat = /^((?:@[^/]+\/)?[^/]+)(\/.*)?$/;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ function rewriteValue(/* StringLiteral */ node, dependencies) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function unpkgRewrite(dependencies = {}) {
|
export default function unpkgRewrite(dependencies = {}) {
|
||||||
return {
|
return {
|
||||||
manipulateOptions(opts, parserOpts) {
|
manipulateOptions(opts, parserOpts) {
|
||||||
parserOpts.plugins.push(
|
parserOpts.plugins.push(
|
||||||
|
@ -88,5 +88,3 @@ function unpkgRewrite(dependencies = {}) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = unpkgRewrite;
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const forge = require('node-forge');
|
import forge from 'node-forge';
|
||||||
const invariant = require('invariant');
|
import invariant from 'invariant';
|
||||||
|
|
||||||
let secretKey;
|
let secretKey;
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
@ -28,4 +28,4 @@ if (process.env.NODE_ENV === 'production') {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = secretKey;
|
export default secretKey;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue