pull/152/head
Alex Kern 5 years ago
parent b830677033
commit 5c3306b9c6
No known key found for this signature in database
GPG Key ID: F3141D5EDF48F89F

@ -1,6 +1,9 @@
'use strict';
module.exports = {
parserOptions: {
project: './tsconfig.json'
},
extends: [
'@strv/typescript',
'@strv/typescript/optional',

@ -50,7 +50,9 @@
"@types/node": "^14.11.1",
"@types/react": "^16.9.49",
"@typescript-eslint/eslint-plugin": "^4.1.1",
"@typescript-eslint/parser": "^4.1.1",
"eslint": "^7.9.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^3.1.4",
"prettier": "^2.1.2",

@ -1,9 +1,8 @@
import alt from '../alt'
import alt from '../alt';
export default alt.createActions(class DownloadActions {
constructor() {
this.generateActions(
'requestDownload'
)
}
})
constructor() {
this.generateActions('requestDownload');
}
},
)

@ -1,10 +1,8 @@
import alt from '../alt'
import alt from '../alt';
export default alt.createActions(class SupportActions {
constructor() {
this.generateActions(
'isChrome',
'noSupport'
)
}
})
constructor() {
this.generateActions('isChrome', 'noSupport');
}
},
)

@ -1,9 +1,8 @@
import alt from '../alt'
import alt from '../alt';
export default alt.createActions(class UploadActions {
constructor() {
this.generateActions(
'uploadFile'
)
}
})
constructor() {
this.generateActions('uploadFile');
}
},
)

@ -1,2 +1,3 @@
import Alt from 'alt'
import Alt from 'alt';
export default new Alt()

@ -2,21 +2,24 @@ import "babel-polyfill";
import "./index.styl";
import React from "react";
import ReactRouter from "react-router";
import routes from "./routes";
import alt from "./alt";
import webrtcSupport from "webrtcsupport";
import webrtcSupport from 'webrtcsupport';
import routes from './routes';
import alt from './alt';
import SupportActions from "./actions/SupportActions";
let bootstrap = document.getElementById("bootstrap").innerHTML;
const bootstrap = document.getElementById("bootstrap").innerHTML;
alt.bootstrap(bootstrap);
window.FilePizza = () => {
ReactRouter.run(routes, ReactRouter.HistoryLocation, function(Handler) {
ReactRouter.run(routes, ReactRouter.HistoryLocation, Handler => {
React.render(<Handler data={bootstrap} />, document);
});
})
if (!webrtcSupport.support) {
SupportActions.noSupport();
}
if (!webrtcSupport.support) SupportActions.noSupport();
let isChrome = navigator.userAgent.toLowerCase().indexOf("chrome") > -1;
if (isChrome) SupportActions.isChrome();
const isChrome = navigator.userAgent.toLowerCase().includes('chrome')
;if (isChrome) {
SupportActions.isChrome()
}
};

@ -1,14 +1,14 @@
import Bootstrap from "./Bootstrap";
import ErrorPage from "./ErrorPage";
import FrozenHead from "react-frozenhead";
import React from "react";
import SupportStore from "../stores/SupportStore";
import { RouteHandler } from "react-router";
import ga from "react-google-analytics";
import { RouteHandler } from 'react-router';
import ga from 'react-google-analytics';
import SupportStore from '../stores/SupportStore';
import ErrorPage from "./ErrorPage";
import Bootstrap from './Bootstrap';
if (process.env.GA_ACCESS_TOKEN) {
ga("create", process.env.GA_ACCESS_TOKEN, "auto");
ga("send", "pageview");
;;ga('send', 'pageview')
}
export default class App extends React.Component {
@ -18,7 +18,7 @@ export default class App extends React.Component {
this._onChange = () => {
this.setState(SupportStore.getState());
};
}
}
componentDidMount() {
@ -62,7 +62,13 @@ export default class App extends React.Component {
</div>
<footer className="footer">
<p>
<strong>Like FilePizza?</strong> Support its development! <a href="https://commerce.coinbase.com/checkout/247b6ffe-fb4e-47a8-9a76-e6b7ef83ea22" className="donate-button">donate</a>
<strong>Like FilePizza?</strong> Support its development!{' '}
<a
href="https://commerce.coinbase.com/checkout/247b6ffe-fb4e-47a8-9a76-e6b7ef83ea22"
className="donate-button"
>
donate
</a>
</p>
<p className="byline">
@ -85,7 +91,7 @@ export default class App extends React.Component {
</p>
</footer>
<script>FilePizza()</script>
{ process.env.GA_ACCESS_TOKEN ? <ga.Initializer /> : <div></div> }
{process.env.GA_ACCESS_TOKEN ? <ga.Initializer /> : <div></div>}
</body>
</html>
);

@ -1,12 +1,13 @@
import React from 'react'
import React from 'react';
export default class Bootstrap extends React.Component {
render() {
return <script
id="bootstrap"
type="application/json"
dangerouslySetInnerHTML={{ __html: this.props.data}} />
return (
<script
id="bootstrap"
type="application/json"
dangerouslySetInnerHTML={{ __html: this.props.data }}
/>
)
}
}

@ -1,4 +1,4 @@
import React from 'react'
import React from 'react';
export default class DownloadButton extends React.Component {
constructor() {
@ -11,15 +11,14 @@ export default class DownloadButton extends React.Component {
}
render() {
return <button
className="download-button"
onClick={this.onClick}>
Download
</button>
return (
<button className="download-button" onClick={this.onClick}>
Download
</button>
)
}
}
DownloadButton.propTypes = {
onClick: React.PropTypes.func.isRequired
onClick: React.PropTypes.func.isRequired,
}

@ -1,22 +1,21 @@
import ChromeNotice from './ChromeNotice'
import DownloadActions from '../actions/DownloadActions'
import DownloadButton from './DownloadButton'
import DownloadStore from '../stores/DownloadStore'
import ErrorPage from './ErrorPage'
import ProgressBar from './ProgressBar'
import React from 'react'
import Spinner from './Spinner'
import { formatSize } from '../util'
import React from "react";
import DownloadActions from '../actions/DownloadActions';
import DownloadStore from '../stores/DownloadStore';
import { formatSize } from "../util";
import ChromeNotice from './ChromeNotice';
import DownloadButton from "./DownloadButton";
import ErrorPage from './ErrorPage';
import ProgressBar from './ProgressBar';
import Spinner from "./Spinner";
export default class DownloadPage extends React.Component {
constructor() {
super()
this.state = DownloadStore.getState()
this._onChange = () => {
this.setState(DownloadStore.getState())
}
};
this.downloadFile = this.downloadFile.bind(this)
}
@ -36,51 +35,69 @@ export default class DownloadPage extends React.Component {
render() {
switch (this.state.status) {
case 'ready':
return <div className="page">
<h1>FilePizza</h1>
<Spinner dir="down"
name={this.state.fileName}
size={this.state.fileSize} />
<ChromeNotice />
<p className="notice">Peers: {this.state.peers} &middot; Up: {formatSize(this.state.speedUp)} &middot; Down: {formatSize(this.state.speedDown)}</p>
<DownloadButton onClick={this.downloadFile} />
</div>
return (
<div className="page">
<h1>FilePizza</h1>
<Spinner
dir="down"
name={this.state.fileName}
size={this.state.fileSize}
/>
<ChromeNotice />
<p className="notice">
Peers: {this.state.peers} &middot; Up:{' '}
{formatSize(this.state.speedUp)} &middot; Down:{' '}
{formatSize(this.state.speedDown)}
</p>
<DownloadButton onClick={this.downloadFile} />
</div>
)
case 'requesting':
case 'downloading':
return <div className="page">
<h1>FilePizza</h1>
<Spinner dir="down" animated
name={this.state.fileName}
size={this.state.fileSize} />
<ChromeNotice />
<p className="notice">Peers: {this.state.peers} &middot; Up: {formatSize(this.state.speedUp)} &middot; Down: {formatSize(this.state.speedDown)}</p>
<ProgressBar value={this.state.progress} />
</div>
return (
<div className="page">
<h1>FilePizza</h1>
<Spinner
dir="down"
animated
name={this.state.fileName}
size={this.state.fileSize}
/>
<ChromeNotice />
<p className="notice">
Peers: {this.state.peers} &middot; Up:{' '}
{formatSize(this.state.speedUp)} &middot; Down:{' '}
{formatSize(this.state.speedDown)}
</p>
<ProgressBar value={this.state.progress} />
</div>
)
case 'done':
return <div className="page">
<h1>FilePizza</h1>
<Spinner dir="down"
name={this.state.fileName}
size={this.state.fileSize} />
<ChromeNotice />
<p className="notice">Peers: {this.state.peers} &middot; Up: {formatSize(this.state.speedUp)} &middot; Down: {formatSize(this.state.speedDown)}</p>
<ProgressBar value={1} />
</div>
return (
<div className="page">
<h1>FilePizza</h1>
<Spinner
dir="down"
name={this.state.fileName}
size={this.state.fileSize}
/>
<ChromeNotice />
<p className="notice">
Peers: {this.state.peers} &middot; Up:{' '}
{formatSize(this.state.speedUp)} &middot; Down:{' '}
{formatSize(this.state.speedDown)}
</p>
<ProgressBar value={1} />
</div>
)
default:
return <ErrorPage />
}
}
}

@ -1,7 +1,6 @@
import React from 'react'
import React from 'react';
export default class DropZone extends React.Component {
constructor() {
super()
this.state = { focus: false }
@ -17,41 +16,49 @@ export default class DropZone extends React.Component {
}
onDragLeave(e) {
if (e.target !== this.refs.overlay.getDOMNode()) return
if (e.target !== this.refs.overlay.getDOMNode()) {
return
}
this.setState({ focus: false })
}
onDragOver(e) {
e.preventDefault()
e.dataTransfer.dropEffect = 'copy'
e.dataTransfer.dropEffect = 'copy';
}
onDrop(e) {
e.preventDefault()
this.setState({ focus: false })
let file = e.dataTransfer.files[0]
if (this.props.onDrop && file) this.props.onDrop(file)
const file = e.dataTransfer.files[0]
if (this.props.onDrop && file) {
this.props.onDrop(file)
}
}
render() {
return <div className="drop-zone" ref="root"
onDragEnter={this.onDragEnter}
onDragLeave={this.onDragLeave}
onDragOver={this.onDragOver}
onDrop={this.onDrop}>
<div className="drop-zone-overlay"
hidden={!this.state.focus}
ref="overlay" />
return (
<div
className="drop-zone"
ref="root"
onDragEnter={this.onDragEnter}
onDragLeave={this.onDragLeave}
onDragOver={this.onDragOver}
onDrop={this.onDrop}
>
<div
className="drop-zone-overlay"
hidden={!this.state.focus}
ref="overlay"
/>
{this.props.children}
</div>
{this.props.children}
</div>
)
}
}
DropZone.propTypes = {
onDrop: React.PropTypes.func.isRequired
onDrop: React.PropTypes.func.isRequired,
}

@ -1,16 +1,15 @@
import ErrorStore from '../stores/ErrorStore'
import React from 'react'
import Spinner from './Spinner'
import React from "react";
import ErrorStore from "../stores/ErrorStore";
import Spinner from './Spinner';
export default class ErrorPage extends React.Component {
constructor() {
super()
this.state = ErrorStore.getState()
this._onChange = () => {
this.setState(ErrorStore.getState())
}
};
}
componentDidMount() {
@ -22,20 +21,17 @@ export default class ErrorPage extends React.Component {
}
render() {
return <div className="page">
<Spinner dir="up" />
<h1 className="with-subtitle">FilePizza</h1>
<p className="subtitle">
<strong>{this.state.status}:</strong> {this.state.message}
</p>
{this.state.stack
? <pre>{this.state.stack}</pre>
: null}
</div>
return (
<div className="page">
<Spinner dir="up" />
<h1 className="with-subtitle">FilePizza</h1>
<p className="subtitle">
<strong>{this.state.status}:</strong> {this.state.message}
</p>
{this.state.stack ? <pre>{this.state.stack}</pre> : null}
</div>
)
}
}

@ -1,43 +1,42 @@
import React from 'react'
import classnames from 'classnames'
import React from "react";
import classnames from "classnames";
function formatProgress(dec) {
return (dec * 100).toPrecision(3) + "%"
return `${(dec * 100).toPrecision(3) }%`
}
export default class ProgressBar extends React.Component {
render() {
const failed = this.props.value < 0;
const inProgress = this.props.value < 1 && this.props.value >= 0;
const classes = classnames('progress-bar', {
'progress-bar-failed': failed,
'progress-bar-in-progress': inProgress,
'progress-bar-small': this.props.small
})
const failed = this.props.value < 0
const inProgress = this.props.value < 1 && this.props.value >= 0
const classes = classnames("progress-bar", {
"progress-bar-failed": failed,
"progress-bar-in-progress": inProgress,
"progress-bar-small": this.props.small,
});
const formatted = formatProgress(this.props.value)
const formatted = formatProgress(this.props.value);
return <div className={classes}>
{failed
? <div className="progress-bar-text">Failed</div>
: inProgress ? <div
className="progress-bar-inner"
style={{width: formatted}}>
<div className="progress-bar-text">
{formatted}
return (
<div className={classes}>
{failed ? (
<div className="progress-bar-text">Failed</div>
) : inProgress ? (
<div className="progress-bar-inner" style={{ width: formatted }}>
<div className="progress-bar-text">{formatted}</div>
</div>
</div>
: <div className="progress-bar-text">Delivered</div>}
</div>
) : (
: <div className="progress-bar-text">Delivered</div>}
</div>
);
}
}
ProgressBar.propTypes = {
value: React.PropTypes.number.isRequired,
small: React.PropTypes.bool
}
small: React.PropTypes.bool,
};
ProgressBar.defaultProps = {
small: false
}
small: false,
};

@ -1,38 +1,41 @@
import React from 'react'
import classnames from 'classnames'
import { formatSize } from '../util'
import React from "react";
import classnames from "classnames";
import { formatSize } from "../util";
export default class Spinner extends React.Component {
render() {
const classes = classnames('spinner', {
'spinner-animated': this.props.animated
})
const classes = classnames("spinner", {
"spinner-animated": this.props.animated,
});
return <div className={classes}>
<img
alt={this.props.name || this.props.dir}
src={`/images/${this.props.dir}.png`}
className="spinner-image" />
return (
<div className={classes}>
<img
alt={this.props.name || this.props.dir}
src={`/images/${this.props.dir}.png`}
className="spinner-image"
/>
{this.props.name === null ? null
: <div className="spinner-name">{this.props.name}</div>}
{this.props.size === null ? null
: <div className="spinner-size">{formatSize(this.props.size)}</div>}
</div>
{this.props.name === null ? null
: <div className="spinner-name">{this.props.name}</div>
)}
{this.props.size === null ? null
: <div className="spinner-size">{formatSize(this.props.size)}</div>
)}
</div>
);
}
}
Spinner.propTypes = {
dir: React.PropTypes.oneOf(['up', 'down']).isRequired,
dir: React.PropTypes.oneOf(["up", "down"]).isRequired,
name: React.PropTypes.string,
size: React.PropTypes.number,
animated: React.PropTypes.bool
}
animated: React.PropTypes.bool,
};
Spinner.defaultProps = {
name: null,
size: null,
animated: false
}
animated: false,
};

@ -1,5 +1,5 @@
import React from 'react'
import QRCode from 'react-qr'
import React from 'react';
import QRCode from 'react-qr';
export default class Tempalink extends React.Component {
constructor() {
@ -12,27 +12,25 @@ export default class Tempalink extends React.Component {
}
render() {
var url = window.location.origin + '/' + this.props.token
var shortUrl = window.location.origin + '/download/' + this.props.shortToken
const url = `${window.location.origin}/${this.props.token}`;
const shortUrl
= window.location.origin + '/download/' + this.props.shortToken
return <div className="tempalink">
<div className="qr">
<QRCode text={url} />
</div>
<div className="urls">
<div className="long-url">
<input
onClick={this.onClick}
readOnly
type="text"
value={url} />
return (
<div className="tempalink">
<div className="qr">
<QRCode text={url} />
</div>
<div className="urls">
<div className="long-url">
<input onClick={this.onClick} readOnly type="text" value={url} />
</div>
<div className="short-url">
or, for short: <span>{shortUrl}</span>
<div className="short-url">
or, for short: <span>{shortUrl}</span>
</div>
</div>
</div>
</div>
)
}
}

@ -1,90 +1,104 @@
import DropZone from './DropZone'
import React from 'react'
import Spinner from './Spinner'
import Tempalink from './Tempalink'
import UploadActions from '../actions/UploadActions'
import UploadStore from '../stores/UploadStore'
import socket from 'filepizza-socket'
import { formatSize } from '../util'
import DropZone from './DropZone'
import Spinner from "./Spinner";
import Tempalink from "./Tempalink";
import UploadActions from "../actions/UploadActions";
import UploadStore from "../stores/UploadStore";
import socket from "filepizza-socket";
import { formatSize } from "../util";
export default class UploadPage extends React.Component {
constructor() {
super()
this.state = UploadStore.getState()
super();
this.state = UploadStore.getState();
this._onChange = () => {
this.setState(UploadStore.getState())
}
this.setState(UploadStore.getState());
};
this.uploadFile = this.uploadFile.bind(this)
this.uploadFile = this.uploadFile.bind(this);
}
componentDidMount() {
UploadStore.listen(this._onChange)
UploadStore.listen(this._onChange);
}
componentWillUnmount() {
UploadStore.unlisten(this._onChange)
UploadStore.unlisten(this._onChange);
}
uploadFile(file) {
UploadActions.uploadFile(file)
UploadActions.uploadFile(file);
}
handleSelectedFile(event) {
let files = event.target.files
const files = event.target.files;
if (files.length > 0) {
UploadActions.uploadFile(files[0])
UploadActions.uploadFile(files[0]);
}
}
render() {
switch (this.state.status) {
case 'ready':
return <DropZone onDrop={this.uploadFile}>
case "ready":
return (
<DropZone onDrop={this.uploadFile}>
<div className="page">
<Spinner dir="up" />
<h1>FilePizza</h1>
<p>Free peer-to-peer file transfers in your browser.</p>
<small className="notice">
We never store anything. Files only served fresh.
</small>
<p>
<label className="select-file-label">
<input
type="file"
onChange={this.handleSelectedFile}
required
/>
<span>select a file</span>
</label>
</p>
</div>
</DropZone>
);
case "processing":
return (
<div className="page">
<Spinner dir="up" animated />
<Spinner dir="up" />
<h1>FilePizza</h1>
<p>Processing...</p>
</div>
);
case "uploading":
return (
<div className="page">
<h1>FilePizza</h1>
<p>Free peer-to-peer file transfers in your browser.</p>
<small className="notice">We never store anything. Files only served fresh.</small>
<Spinner
dir="up"
animated
name={this.state.fileName}
size={this.state.fileSize}
/>
<p>Send someone this link to download.</p>
<small className="notice">
This link will work as long as this page is open.
</small>
<p>
<label className="select-file-label">
<input type="file" onChange={this.handleSelectedFile} required/>
<span>select a file</span>
</label>
Peers: {this.state.peers} &middot; Up:{" "}
{formatSize(this.state.speedUp)}
</p>
<Tempalink
token={this.state.token}
shortToken={this.state.shortToken}
/>
</div>
</DropZone>
case 'processing':
return <div className="page">
<Spinner dir="up" animated />
<h1>FilePizza</h1>
<p>Processing...</p>
</div>
case 'uploading':
return <div className="page">
<h1>FilePizza</h1>
<Spinner dir="up" animated
name={this.state.fileName}
size={this.state.fileSize} />
<p>Send someone this link to download.</p>
<small className="notice">This link will work as long as this page is open.</small>
<p>Peers: {this.state.peers} &middot; Up: {formatSize(this.state.speedUp)}</p>
<Tempalink token={this.state.token} shortToken={this.state.shortToken} />
</div>
);
}
}
}

@ -15,27 +15,32 @@ export default class UploadPage extends React.Component {
render() {
switch (this.props.status) {
case 'ready':
return <div>
<DropZone onDrop={this.uploadFile} />
<Arrow dir="up" />
</div>;
break;
return (
<div>
<DropZone onDrop={this.uploadFile} />
<Arrow dir="up" />
</div>
)
break
case 'processing':
return <div>
<Arrow dir="up" animated />
<FileDescription file={this.props.file} />
</div>;
break;
return (
<div>
<Arrow dir="up" animated />
<FileDescription file={this.props.file} />
</div>
)
break
case 'uploading':
return <div>
<Arrow dir="up" animated />
<FileDescription file={this.props.file} />
<Temaplink token={this.props.token} />
</div>;
break;
return (
<div>
<Arrow dir="up" animated />
<FileDescription file={this.props.file} />
<Temaplink token={this.props.token} />
</div>
)
break
}
}
}

@ -1,46 +1,44 @@
import toppings from './toppings'
import xkcdPassword from 'xkcd-password'
import xkcdPassword from "xkcd-password";
import toppings from "./toppings";
const TOKEN_OPTIONS = {
numWords: 4,
minLength: 3,
maxLength: 20
maxLength: 20,
}
const SHORT_TOKEN_OPTIONS = {
length: 8,
chars: '0123456789abcdefghijklmnopqrstuvwxyz'
chars: '0123456789abcdefghijklmnopqrstuvwxyz',
}
let tokens = {}
let shortTokens = {}
const tokens = {}
const shortTokens = {}
const tokenGenerator = new xkcdPassword()
tokenGenerator.initWithWordList(toppings)
function generateShortToken() {
var result = '';
for (var i = SHORT_TOKEN_OPTIONS.length; i > 0; --i)
result += SHORT_TOKEN_OPTIONS.chars[Math.floor(Math.random() * SHORT_TOKEN_OPTIONS.chars.length)];
let result = '';
for (let i = SHORT_TOKEN_OPTIONS.length; i > 0; --i) {
{ result += SHORT_TOKEN_OPTIONS.chars[Math.floor(Math.random() * SHORT_TOKEN_OPTIONS.chars.length)]}
return result;
}
export function create(socket) {
return tokenGenerator.generate(TOKEN_OPTIONS).then((parts) => {
const token = parts.join('/')
const shortToken = generateShortToken()
let result = {
token: token,
shortToken: shortToken,
socket: socket
;const shortToken = generateShortToken()
const result = {
token,
shortToken,
socket,
}
tokens[token] = result
shortTokens[shortToken] = result
return result
})
});
}
export function find(token) {
@ -50,9 +48,9 @@ export function find(token) {
export function findShort(shortToken) {
return shortTokens[shortToken.toLowerCase()]
}
export function remove(client) {
if (client == null) return
if (client == null) { return }
delete tokens[client.token]
delete shortTokens[client.shortToken]
}

@ -1,48 +1,51 @@
var twilio = require("twilio");
const twilio = require("twilio");
var winston = require("winston");
if (process.env.TWILIO_SID && process.env.TWILIO_TOKEN) {
var twilioSID = process.env.TWILIO_SID;
var twilioToken = process.env.TWILIO_TOKEN;
const twilioSID = process.env.TWILIO_SID;
const twilioToken = process.env.TWILIO_TOKEN;
var client = twilio(twilioSID, twilioToken);
winston.info("Using Twilio TURN service");
} else {
var client = null;
}
var ICE_SERVERS = [
let ICE_SERVERS = [
{
urls: "stun:stun.l.google.com:19302"
}
urls: "stun:stun.l.google.com:19302",
},
];
if (process.env.ICE_SERVERS) {
ICE_SERVERS = JSON.parse(process.env.ICE_SERVERS)
}
var CACHE_LIFETIME = 5 * 60 * 1000; // 5 minutes
var cachedPromise = null;
const CACHE_LIFETIME = 5 * 60 * 1000; // 5 minutes
let cachedPromise = null;
function clearCache() {
cachedPromise = null;
}
exports.getICEServers = function() {
if (client == null) return Promise.resolve(ICE_SERVERS);
if (cachedPromise) return cachedPromise;
if (client == null) {
return Promise.resolve(ICE_SERVERS)
}
if (cachedPromise) {
return cachedPromise
}
cachedPromise = new Promise(function(resolve, reject) {
client.tokens.create({}, function(err, token) {
cachedPromise = new Promise((resolve, reject) => {
client.tokens.create({}, (err, token) => {
if (err) {
winston.error(err);
return resolve(DEFAULT_ICE_SERVERS);
}
winston.info("Retrieved ICE servers from Twilio");
setTimeout(clearCache, CACHE_LIFETIME);
;setTimeout(clearCache, CACHE_LIFETIME)
resolve(token.ice_servers);
});
})
});
return cachedPromise;
};
}

@ -2,7 +2,7 @@
try {
require('../newrelic')
require('newrelic')
;require('newrelic');
} catch (ex) {
// Don't load New Relic if the configuration file doesn't exist.
}
@ -10,15 +10,14 @@ try {
process.on('unhandledRejection', (reason, p) => {
p.catch(err => {
console.error('Exiting due to unhandled rejection!')
console.error(err)
;;;console.error(err)
process.exit(1)
})
});
})
process.on('uncaughtException', err => {
console.error('Exiting due to uncaught exception!')
console.error(err.stack)
;console.error(err.stack)
process.exit(1)
})
module.exports = require('./server')
});
module.exports = require("./server");

@ -1,9 +1,7 @@
var db = require('../db')
var express = require('express')
var routes = module.exports = new express.Router()
function bootstrap (uploader, req, res, next) {
const express = require("express");
const db = require("../db");
let routes = (module.exports = new express.Router())
;function bootstrap(uploader, req, res, next) {
if (uploader) {
res.locals.data = {
DownloadStore: {
@ -12,24 +10,23 @@ function bootstrap (uploader, req, res, next) {
fileSize: uploader.fileSize,
fileName: uploader.fileName,
fileType: uploader.fileType,
infoHash: uploader.infoHash
}
infoHash: uploader.infoHash,
},
}
next()
} else {
var err = new Error('Not Found')
err.status = 404
const err = new Error('Not Found')
;err.status = 404
next(err)
}
}
routes.get(/^\/([a-z]+\/[a-z]+\/[a-z]+\/[a-z]+)$/, function (req, res, next) {
var uploader = db.find(req.params[0])
routes.get(/^\/([a-z]+\/[a-z]+\/[a-z]+\/[a-z]+)$/, (req, res, next) => {
const uploader = db.find(req.params[0])
return bootstrap(uploader, req, res, next)
})
routes.get(/^\/download\/(\w+)$/, function (req, res, next) {
var uploader = db.findShort(req.params[0])
});
routes.get(/^\/download\/(\w+)$/, (req, res, next) => {
const uploader = db.findShort(req.params[0])
return bootstrap(uploader, req, res, next)
})
});

@ -1,19 +1,18 @@
module.exports = function (err, req, res, next) {
const status = err.status || 500;
const message = err.message || '';
const stack
= process.env.NODE_ENV === 'production' ? null : err.stack || null
var status = err.status || 500
var message = err.message || ''
var stack = process.env.NODE_ENV === 'production' ? null : err.stack || null
req.url = '/error'
req.url = '/error';
res.status(status)
res.locals.data = {
ErrorStore: {
status: status,
message: message,
stack: stack
}
status,
message,
stack,
},
}
next()
}
};

@ -1,16 +1,15 @@
const path = require('path')
const BUNDLE_PATH = path.resolve(__dirname, '../../dist/bundle.js')
if (process.env.NODE_ENV === 'production') {
;const BUNDLE_PATH = path.resolve(__dirname, '../../dist/bundle.js')
;if (process.env.NODE_ENV === 'production') {
module.exports = function (req, res) {
res.sendFile(BUNDLE_PATH)
}
};
} else {
const webpackMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
const config = require('../../webpack.config.js')
config.output.filename = '/app.js'
config.output.path = '/'
;const webpack = require('webpack')
;const config = require('../../webpack.config.js')
;config.output.filename = '/app.js';
config.output.path = '/';
module.exports = webpackMiddleware(webpack(config))
}

@ -1,30 +1,29 @@
var React = require('react')
var ReactRouter = require('react-router')
var alt = require('../alt')
var routes = require('../routes')
function isNotFound(state) {
for (var r of state.routes) {
if (r.isNotFound) return true
const React = require('react')
;let ReactRouter = require('react-router')
;let alt = require('../alt')
;let routes = require('../routes')
;function isNotFound(state) {
for (const r of state.routes) {
if (r.isNotFound) {
return true
}
}
return false
}
module.exports = function (req, res) {
alt.bootstrap(JSON.stringify(res.locals.data || {}))
ReactRouter.run(routes, req.url, function (Handler, state) {
var html = React.renderToString(<Handler data={alt.takeSnapshot()} />)
ReactRouter.run(routes, req.url, (Handler, state) => {
const html = React.renderToString(<Handler data={alt.takeSnapshot()} />);
alt.flush()
res.setHeader('Content-Type', 'text/html');
if (isNotFound(state)) res.status(404)
;;if (isNotFound(state)) {
res.status(404);
}
res.write('<!DOCTYPE html>\n')
res.end(html)
})
;;res.end(html)
});
}

@ -1,6 +1,4 @@
var express = require('express')
var path = require('path')
var STATIC_PATH = path.resolve(__dirname, '../static')
module.exports = express.static(STATIC_PATH)
const path = require("path");
const express = require("express");
let STATIC_PATH = path.resolve(__dirname, "../static");
module.exports = express.static(STATIC_PATH);

@ -1,10 +1,9 @@
import React from 'react'
import { Route, DefaultRoute, NotFoundRoute, RouteHandler } from 'react-router'
import App from './components/App'
import DownloadPage from './components/DownloadPage'
import UploadPage from './components/UploadPage'
import ErrorPage from './components/ErrorPage'
import React from 'react';
import { Route, DefaultRoute, NotFoundRoute, RouteHandler } from 'react-router';
import App from './components/App';
import DownloadPage from './components/DownloadPage';
import UploadPage from './components/UploadPage';
import ErrorPage from './components/ErrorPage';
export default (
<Route handler={App}>
@ -15,4 +14,3 @@ export default (
<NotFoundRoute handler={ErrorPage} />
</Route>
)

@ -1,35 +1,33 @@
var db = require("./db");
var express = require("express");
const fs = require("fs");
const express = require("express");
var expressWinston = require("express-winston");
var fs = require("fs");
var ice = require("./ice");
var socketIO = require("socket.io");
var winston = require("winston");
process.on("unhandledRejection", (reason, p) => {
var ice = require('./ice')
;var socketIO = require('socket.io')
;var winston = require('winston')
;const db = require('./db')
;process.on("unhandledRejection", (reason, p) => {
p.catch(err => {
log.error("Exiting due to unhandled rejection!");
log.error(err);
process.exit(1);
});
})
});
process.on("uncaughtException", err => {
log.error("Exiting due to uncaught exception!");
log.error(err);
process.exit(1);
});
var app = express();
var port =
process.env.PORT || (process.env.NODE_ENV === "production" ? 80 : 3000);
})
const app = express();
let port
process.env.PORT || (process.env.NODE_ENV === "production" ? 80 : 3000);
if (!process.env.QUIET) {
app.use(
expressWinston.logger({
winstonInstance: winston,
expressFormat: true
})
winstonInstance: winston,
expressFormat: true,
})
);
}
@ -39,7 +37,7 @@ app.use(require("./middleware/static"));
app.use([
require("./middleware/bootstrap"),
require("./middleware/error"),
require("./middleware/react")
require("./middleware/react"),
]);
const TRACKERS = process.env.WEBTORRENT_TRACKERS
@ -47,18 +45,19 @@ const TRACKERS = process.env.WEBTORRENT_TRACKERS
: [
["wss://tracker.openwebtorrent.com"],
["wss://tracker.btorrent.xyz"],
["wss://tracker.fastcast.nz"]
];
function bootServer(server) {
var io = socketIO(server);
['wss://tracker.fastcast.nz'],
]
;function bootServer(server) {
const io = socketIO(server);
io.set("transports", ["polling"]);
io.on("connection", function(socket) {
var upload = null;
io.on("connection", socket => {
let upload = null;
socket.on("upload", function(metadata, res) {
if (upload) return;
socket.on("upload", (metadata, res) => {
if (upload) {
return
}
db.create(socket).then(u => {
upload = u;
upload.fileName = metadata.fileName;
@ -66,43 +65,45 @@ function bootServer(server) {
upload.fileType = metadata.fileType;
upload.infoHash = metadata.infoHash;
res({ token: upload.token, shortToken: upload.shortToken });
});
})
});
socket.on("trackerConfig", function(_, res) {
ice.getICEServers().then(function(iceServers) {
socket.on("trackerConfig", (_, res) => {
ice.getICEServers().then(iceServers => {
res({ rtcConfig: { iceServers }, announce: TRACKERS });
});
})
});
socket.on("disconnect", function() {
socket.on("disconnect", () => {
db.remove(upload);
});
})
});
server.on("error", function(err) {
server.on("error", err => {
winston.error(err.message);
process.exit(1);
});
server.listen(port, function(err) {
var host = server.address().address;
var port = server.address().port;
})
server.listen(port, (err) => {
const host = server.address().address;
const port = server.address().port;
winston.info("FilePizza listening on %s:%s", host, port);
});
})
}
if (process.env.HTTPS_KEY && process.env.HTTPS_CERT) {
// user-supplied HTTPS key/cert
var https = require("https");
var server = https.createServer({
key: fs.readFileSync(process.env.HTTPS_KEY),
cert: fs.readFileSync(process.env.HTTPS_CERT),
}, app)
const https = require("https");
var server = https.createServer(
{
key: fs.readFileSync(process.env.HTTPS_KEY),
cert: fs.readFileSync(process.env.HTTPS_CERT),
},
app,
)
bootServer(server)
} else {
// no HTTPS
var http = require("http");
const http = require("http");
var server = http.Server(app)
bootServer(server)
}

@ -1,73 +1,82 @@
import DownloadActions from '../actions/DownloadActions'
import alt from '../alt'
import socket from 'filepizza-socket'
import { getClient } from '../wt'
import socket from "filepizza-socket";
import DownloadActions from "../actions/DownloadActions";
import alt from "../alt";
import { getClient } from '../wt';
const SPEED_REFRESH_TIME = 2000
function downloadBlobURL(name, blobURL) {
let a = document.createElement('a')
document.body.appendChild(a)
const a = document.createElement('a')
;document.body.appendChild(a)
a.download = name
a.href = blobURL
a.click()
}
export default alt.createStore(class DownloadStore {
export default alt.createStore(
class DownloadStore {
constructor() {
this.bindActions(DownloadActions)
constructor() {
this.bindActions(DownloadActions)
this.fileName = "";
this.fileSize = 0
this.fileType = "";
this.infoHash = null
this.peers = 0;
this.progress = 0;
this.speedDown = 0;
this.speedUp = 0;
this.status = "uninitialized";
this.token = null
}
this.fileName = ''
this.fileSize = 0
this.fileType = ''
this.infoHash = null
this.peers = 0
this.progress = 0
this.speedDown = 0
this.speedUp = 0
this.status = 'uninitialized'
this.token = null
}
onRequestDownload() {
if (this.status !== 'ready') {
return
}
this.status = 'requesting';
onRequestDownload() {
if (this.status !== 'ready') return
this.status = 'requesting'
getClient().then(client => {
client.add(
this.infoHash,
{ announce: client.tracker.announce },
(torrent) => {
this.setState({ status: 'downloading' })
getClient().then(client => {
client.add(this.infoHash, { announce: client.tracker.announce }, (torrent) => {
this.setState({ status: 'downloading' })
const updateSpeed = () => {
this.setState({
speedUp: torrent.uploadSpeed,
speedDown: torrent.downloadSpeed,
peers: torrent.numPeers,
})
}
torrent.on('upload', updateSpeed)
torrent.on("download", updateSpeed);
setInterval(updateSpeed, SPEED_REFRESH_TIME);
const updateSpeed = () => {
this.setState({
speedUp: torrent.uploadSpeed,
speedDown: torrent.downloadSpeed,
peers: torrent.numPeers
})
}
const file = torrent.files[0];
const stream = file.createReadStream();
stream.on("data", (chunk) => {
if (this.status !== 'downloading') {
return
}
torrent.on('upload', updateSpeed)
torrent.on('download', updateSpeed)
setInterval(updateSpeed, SPEED_REFRESH_TIME)
const file = torrent.files[0]
const stream = file.createReadStream()
stream.on('data', (chunk) => {
if (this.status !== 'downloading') return
if (torrent.progress === 1) {
this.setState({ status: 'done', progress: 1 })
file.getBlobURL((err, blobURL) => {
if (err) throw err
downloadBlobURL(this.fileName, blobURL)
if (torrent.progress === 1) {
this.setState({ status: 'done', progress: 1 })
file.getBlobURL((err, blobURL) => {
if (err) {
throw err
}
downloadBlobURL(this.fileName, blobURL)
});
} else {
this.setState({ progress: torrent.progress })
}
})
} else {
this.setState({ progress: torrent.progress })
}
})
})
})
}
}, 'DownloadStore')
},
)
});
}
},
'DownloadStore'
)

@ -1,20 +1,21 @@
import SupportActions from '../actions/SupportActions'
import alt from '../alt'
import SupportActions from '../actions/SupportActions';
import alt from '../alt';
export default alt.createStore(class ErrorStore {
export default alt.createStore(
class ErrorStore {
constructor() {
this.bindActions(SupportActions)
constructor() {
this.bindActions(SupportActions)
this.status = 404;
this.message = "Not Found";
this.stack = null
}
this.status = 404
this.message = 'Not Found'
this.stack = null
}
onNoSupport() {
this.status = 400
this.message = 'No WebRTC Support. Please use Chrome or Firefox.'
this.stack = null
}
}, 'ErrorStore')
onNoSupport() {
this.status = 400
this.message = "No WebRTC Support. Please use Chrome or Firefox.";
this.stack = null
}
},
'ErrorStore'
)

@ -1,20 +1,21 @@
import SupportActions from '../actions/SupportActions'
import alt from '../alt'
import SupportActions from '../actions/SupportActions';
import alt from '../alt';
export default alt.createStore(class SupportStore {
export default alt.createStore(
class SupportStore {
constructor() {
this.bindActions(SupportActions)
this.isSupported = true;
this.isChrome = false;
}
constructor() {
this.bindActions(SupportActions)
this.isSupported = true
this.isChrome = false
}
onNoSupport() {
this.isSupported = false
}
onNoSupport() {
this.isSupported = false
}
onIsChrome() {
this.isChrome = true
}
}, 'SupportStore')
onIsChrome() {
this.isChrome = true
}
},
'SupportStore'
)

@ -1,6 +1,6 @@
import UploadActions from "../actions/UploadActions";
import alt from "../alt";
import socket from "filepizza-socket";
import socket from 'filepizza-socket';
import UploadActions from '../actions/UploadActions';
import alt from '../alt';
import { getClient } from "../wt";
const SPEED_REFRESH_TIME = 2000;
@ -22,7 +22,9 @@ export default alt.createStore(
}
onUploadFile(file) {
if (this.status !== "ready") return;
if (this.status !== "ready") {
return;
}
this.status = "processing";
getClient().then(client => {
@ -30,9 +32,9 @@ export default alt.createStore(
const updateSpeed = () => {
this.setState({
speedUp: torrent.uploadSpeed,
peers: torrent.numPeers
peers: torrent.numPeers,
});
};
}
torrent.on("upload", updateSpeed);
torrent.on("download", updateSpeed);
@ -44,7 +46,7 @@ export default alt.createStore(
fileName: file.name,
fileSize: file.size,
fileType: file.type,
infoHash: torrent.magnetURI
infoHash: torrent.magnetURI,
},
(res) => {
this.setState({
@ -54,11 +56,11 @@ export default alt.createStore(
fileName: file.name,
fileSize: file.size,
fileType: file.type,
infoHash: torrent.magnetURI
infoHash: torrent.magnetURI,
});
}
},
);
});
})
});
}
},

@ -96,5 +96,5 @@ export default [
"walnuts",
"watercress",
"whitebait",
"zucchini"
"zucchini",
]

@ -1,9 +1,11 @@
// Taken from StackOverflow
// http://stackoverflow.com/questions/15900485/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-in-javascript
export function formatSize(bytes) {
if (bytes === 0) return '0 Bytes'
if (bytes === 0) {
return '0 Bytes';
}
const k = 1000
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i]
;const i = Math.floor(Math.log(bytes) / Math.log(k))
return `${(bytes / Math.pow(k, i)).toPrecision(3)} ${sizes[i]}`;
}

@ -1,12 +1,12 @@
import socket from 'filepizza-socket'
import socket from 'filepizza-socket';
export function getClient() {
return new Promise((resolve, reject) => {
socket.emit('trackerConfig', {}, (trackerConfig) => {
const client = new WebTorrent({
tracker: trackerConfig
tracker: trackerConfig,
})
resolve(client)
})
});
})
}

@ -20,6 +20,7 @@
},
"include": [
"next-env.d.ts",
"**/*.js",
"**/*.ts",
"**/*.tsx"
],

@ -1,12 +1,11 @@
const nib = require("nib");
const webpack = require('webpack')
module.exports = {
;module.exports = {
entry: "./src/client",
target: "web",
output: {
filename: "dist/bundle.js"
filename: "dist/bundle.js",
},
module: {
@ -14,37 +13,39 @@ module.exports = {
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
loader: "babel-loader",
},
{
test: /\.json$/,
loader: "json"
loader: "json",
},
{
test: /\.styl$/,
loader: "style-loader!css-loader!stylus-loader"
}
]
loader: "style-loader!css-loader!stylus-loader",
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'GA_ACCESS_TOKEN': JSON.stringify(process.env.GA_ACCESS_TOKEN),
}
})
GA_ACCESS_TOKEN: JSON.stringify(process.env.GA_ACCESS_TOKEN),
},
}),
],
node: {
fs: "empty"
fs: "empty",
},
stylus: {
use: [nib()]
use: [nib()],
},
rules: [{
test: /react-google-analytics/,
use: process.env.GA_ACCESS_TOKEN ? 'null-loader' : 'noop-loader'
}]
rules: [
{
test: /react-google-analytics/,
use: process.env.GA_ACCESS_TOKEN ? 'null-loader' : 'noop-loader',
},
],
};

@ -1187,6 +1187,16 @@
"@typescript-eslint/typescript-estree" "2.34.0"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/parser@^4.1.1":
version "4.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.1.1.tgz#324b4b35e314075adbc92bd8330cf3ef0c88cf3e"
integrity sha512-NLIhmicpKGfJbdXyQBz9j48PA6hq6e+SDOoXy7Ak6bq1ebGqbgG+fR1UIDAuay6OjQdot69c/URu2uLlsP8GQQ==
dependencies:
"@typescript-eslint/scope-manager" "4.1.1"
"@typescript-eslint/types" "4.1.1"
"@typescript-eslint/typescript-estree" "4.1.1"
debug "^4.1.1"
"@typescript-eslint/scope-manager@4.1.1":
version "4.1.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.1.1.tgz#bdb8526e82435f32b4ccd9dd4cec01af97b48850"
@ -3271,6 +3281,13 @@ escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
eslint-config-prettier@^6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1"
integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==
dependencies:
get-stdin "^6.0.0"
eslint-import-resolver-node@^0.3.3:
version "0.3.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
@ -3901,6 +3918,11 @@ generate-object-property@^1.1.0:
dependencies:
is-property "^1.0.0"
get-stdin@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"

Loading…
Cancel
Save