Skip to content
This repository has been archived by the owner on May 31, 2021. It is now read-only.

Commit

Permalink
Multi rtorrent instances support (#638)
Browse files Browse the repository at this point in the history
* Multi rtorrent instances support (#549)

* Handling multiple rtorrent instances

* performance issue addressing

* fix wrong conflict resolution

* performances

* purge services instance every 10 mins

* last fixes

* avoid duplicated reducers

* Adjusts UI of rtorrent connection form fields (#639)

* Multi rtorrent instance cleanup (#676)

* Cleans up multi-user logic.

* Rename clientRequestService to clientGatewayService

* [multi-user] Adds prompt to fix rtorrent connection failure (#681)

* Adds ability to update user

* Add connection test endpoints and client actions

* Listen to client connectivity change events

* Separates scgiUtil from clientRequestManager

* Adds BaseService

* Services extend BaseService

* Listen for connetion change events in client

* Reorganizes app structure

* Adds prompt to correct per-user client interruption

* Fixes linting errors

* Removes debugging stuff

* Fixes Size rendering

* Fixes moveTorrents

* Adds migration step for legacy database entries (#686)

* Use FormattedMessage

* Updates documentation

* Fixes migration script for new installs

* Updates dependencies

* Moves string to i18n

* Updates deps
  • Loading branch information
jfurrow committed Sep 2, 2018
1 parent cccce36 commit f492ad3
Show file tree
Hide file tree
Showing 60 changed files with 4,819 additions and 4,245 deletions.
32 changes: 5 additions & 27 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,8 @@ pids
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm
Expand All @@ -63,10 +38,13 @@ typings/
# VisualStudio
.vscode

# Personnal flood config
# Intellij
.idea

# Personal flood config
config.js

# flood server
# Flood server
server/db
server/assets
server/temp
Expand Down
99 changes: 42 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,103 +4,88 @@

[![Travis CI build status badge](https://img.shields.io/travis/jfurrow/flood/master.svg?style=flat-square)](https://travis-ci.org/jfurrow/flood) [![Discord server badge](https://img.shields.io/discord/418267176873623553.svg?style=flat-square)](https://discord.gg/Z7yR5Uf)

Flood is another web interface for [rtorrent](https://github.com/rakshasa/rtorrent). It implements a Node.js server for communicating with the rTorrent API, storing historical data, and serving the web UI.

It's a work-in-progress, and it might not have all of the features you want (yet). However, new features are added frequently. Feel free to file an issue and I'll try to prioritize your feature requests.
Flood is a monitoring service for [rTorrent](https://github.com/rakshasa/rtorrent). It's a Node.js service that communicates with rTorrent instances and serves a decent web UI for administration. It's a work-in-progress.

#### Feedback

If you have a specific issue or bug, please file a Github issue. If you want to participate in discussions about Flood's future, please join the [Flood Discord server](https://discord.gg/Z7yR5Uf).
If you have a specific issue or bug, please file a Github issue. Please join the [Flood Discord server](https://discord.gg/Z7yR5Uf) to discuss feature requests and implementation details.

# Usage
# Getting started

#### Pre-Requisites
### Pre-Requisites

1. [rTorrent](https://github.com/rakshasa/rtorrent) needs to be installed __with XMLRPC__ configuration. _If you are currently using a web UI for rTorrent, you've already done this._
* For Linux & OS X, check out [rTorrent's installation wiki](https://github.com/rakshasa/rtorrent/wiki/Installing#compilation-help) and/or [this third-party tutorial](https://jes.sc/kb/rTorrent+ruTorrent-Seedbox-Guide.php#Install-Dependencies). When you run `./configure`, be sure to run with the `--with-xmlrpc-c` flag.
* For Windows, try [this guide](https://rtwi.jmk.hu/wiki/rTorrentOnWindows) (I haven't tested this, let me know if you have problems).
1. [rTorrent](https://github.com/rakshasa/rtorrent) needs to be installed and running __with XMLRPC__ configuration.
* For Linux & OS X, check out [rTorrent's installation wiki](https://github.com/rakshasa/rtorrent/wiki/Installing#compilation-help) and/or [this third-party tutorial](https://jes.sc/kb/rTorrent+ruTorrent-Seedbox-Guide.php#Install-Dependencies). When you run `./configure`, be sure to run with the `--with-xmlrpc-c` flag.
* For Windows, try [this guide](https://rtwi.jmk.hu/wiki/rTorrentOnWindows).
2. Install NodeJS version `8` or higher (you might want to manage different Node versions with [nvm](https://github.com/creationix/nvm) or [n](https://github.com/tj/n)).
3. Since #523 [node-gyp](https://www.npmjs.com/package/node-gyp) is needed.
* `sudo npm install -g node-gyp`
* Check you match node-gyp [dependencies](https://github.com/nodejs/node-gyp#installation) (ex: gcc, make, python2).

#### Configuring
### Configuration

1. Copy `config.template.js` to `config.js`. This is required.
2. Set your rTorrent SCGI hostname and port in `config.js`. Defaults are `localhost` and `5000`.
* If you want to use a socket, change `socket` to true and set `socketPath` to the absolute file path of your rTorrent socket. Make sure Flood has read/write access. Specify the socket path in `.rtorrent.rc`. Example: `scgi_local = /Users/flood/rtorrent.sock`
* If you wish to access an rTorrent instance running on a separate host from Flood (or in a Docker container), allow for incoming connections from external IPs by setting the host in `scgi_port` to `0.0.0.0` in `.rtorrent.rc`. Example: `scgi_port = 0.0.0.0:5000`
3. Create a long, unique secret (used to sign [JWT auth tokens](https://github.com/auth0/node-jsonwebtoken)) in `config.js`.
4. If you're proxying Flood to a path other than the root of the host, you must specify the `baseURI` in `config.js`. All request URIs will be prefixed with this value.
* For example, if hosting Flood from `https://foo.bar/apps/flood`, you would set `baseURI` to `/apps/flood`. If hosting flood from `https://foo.bar`, you do not need to configure `baseURI`. See also [Using Flood behind a reverse proxy](https://github.com/jfurrow/flood/wiki/Using-Flood-behind-a-reverse-proxy) on the wiki.
Copy `config.template.js` to `config.js` and review its comments. **This is required.**

**Note**: each time you modify `baseURI` in `config.js` you need to recompile assets (`npm run build`). To be sure follow the *Updating* procedure each time you modify the `config.js` file.
When loading the web interface, you will be prompted to configure the connection to rtorrent. Other configuration options are handled `config.js`.

#### Compiling assets and starting the server
**What to configure**

1. Run `npm install`.
* Note: Since #523 [node-gyp](https://www.npmjs.com/package/node-gyp) is needed. **IF** you need `sudo`, use `sudo npm i --unsafe-perm` (see [here](https://github.com/nodejs/node-gyp/issues/454) for why `--unsafe-perm`) else installation will fail. If you dont need sudo just use `npm i` as usual.
* If your system use python3 as default you will need to install python2 and use `npm i --python="/usr/bin/python2"`.
1. Be sure to create a long and unique secret (used to sign [JWT auth tokens](https://github.com/auth0/node-jsonwebtoken)).
3. If you are proxying requests to Flood from your own web server, configure Flood's path from the host at the `baseURI` property. All requests will be prefixed with this value.
* For example, if serving Flood from `https://foo.bar/apps/flood`, you would set `baseURI` to `/apps/flood`. If serving flood from `https://foo.bar`, you do not need to configure `baseURI`.
* [Read more about proxying requests to Flood on the Wiki](https://github.com/jfurrow/flood/wiki/Using-Flood-behind-a-reverse-proxy), this is a common pain-point for users.

**Note**: Some of these values are baked into the static assets (like `baseURI`), so changes to this file require recompling static assets.

### Compiling assets and starting the server

From the root of the Flood directory...
1. Run `npm install` if you haven't already or if you've pulled changes.
2. Run `npm run build`.
3. Run `npm start`.
4. Access the UI in your browser. Defaults to `localhost:3000`.
* You may change the default port in `config.js`.
5. Upon loading the UI the first time, you will be prompted to create a user account.

#### Updating
Access the UI in your browser. With default settings, go to `http://localhost:3000`. You can configure the port in `config.js`.

### Updating

I've been bad about cutting actual releases, so check this repo for recent commits.

1. To update, run `git pull` in this repository's directory.
1. Check `config.template.js` for configuration changes that you may wish to incoporate in your `config.js`.
1. Kill the running Node server.
1. Kill the currently running Flood server.
1. Run `npm install` to update dependencies.
1. Run `npm run build` to transpile and bundle static assets.
1. Restart it with `npm start`.
1. Start the Flood server with `npm start`.

#### Tips
### Troubleshooting

* I run the web server with `screen` to keep the web server running independently of the terminal session.
* Ubuntu users may need to install `nodejs-legacy` (`sudo apt-get install nodejs-legacy`) for dependencies to install successfully. You can read more on [this Stack Overflow post](http://stackoverflow.com/questions/21168141/cannot-install-packages-using-node-package-manager-in-ubuntu).
* Ask for help in the [Flood Discord server](https://discord.gg/Z7yR5Uf).

#### Local Development
### Local Development

1. Run `npm install`.
2. Run `npm run start:development:server` and `npm run start:development:client` in separate terminal instances.
* `npm run start:development:server` uses [nodemon](https://github.com/remy/nodemon) to watch for changes to the server-side JavaScript.
* `npm run start:development:client` watches for changes in the client-side source.
3. Access the UI through the [WebpackDevServer](https://webpack.js.org/configuration/dev-server/). It expects to proxy requests to the Flood server you have running, defined in `config.js` as `floodServerProxy`.
4. Build the documentation `npm run build-docs`.
* `npm run start:development:server` uses [nodemon](https://github.com/remy/nodemon) to watch for changes to the server-side JavaScript.
* `npm run start:development:client` watches for changes in the client-side source.
3. Access the UI in your browser. Defaults to `localhost:4200`.

#### Environment Variables
### Environment Variables

1. `DEV_SERVER_PORT`: webpackDevServer's port, used when developing Flood. Defaults to `4200`.
1. `DEV_SERVER_HOST`: webpackDevServer's host, used when developing Flood. Defaults to `0.0.0.0`.
1. `DEV_SERVER_HTTPS`: webpackDevServer's protocol, used when developing Flood. Defaults to `http`.

#### Running with Docker
### Running with Docker

1. `docker build -t rtorrent-flood .`
2. `docker run --name rtorrent-flood -e RTORRENT_SCGI_HOST=w.x.y.z -p 3000:3000 rtorrent-flood`
3. Other supported environment variables:
* `FLOOD_BASE_URI`
* `FLOOD_SECRET`
* `RTORRENT_SCGI_HOST`
* `RTORRENT_SCGI_PORT`
* `RTORRENT_SOCK`
* `FLOOD_ENABLE_SSL`
* `FLOOD_BASE_URI`
* `FLOOD_SECRET`
* `FLOOD_ENABLE_SSL`

The docker container includes a volume at `/data`, which is where the database will be located. Additionally, you can place your SSL files there, `/data/flood_ssl.key` and `/data/flood_ssl.cert`. Set `FLOOD_ENABLE_SSL` to `true` to enable their use if present. Additionally, a local rtorrent socket file located at `/data/rtorrent.sock` can be used if `RTORRENT_SOCK` is set to `true`.

Check out the [Wiki](https://github.com/jfurrow/flood/wiki/Docker) for more information.

## Notes

This project's client-side build tooling is based on the wonderful [create-react-app](https://github.com/facebookincubator/create-react-app).

# Screenshots
## Acknowledgments

![](https://s3.amazonaws.com/johnfurrow.com/share/flood-screenshots-a.png)
![](https://s3.amazonaws.com/johnfurrow.com/share/flood-screenshots-b.png)
![](https://s3.amazonaws.com/johnfurrow.com/share/flood-screenshots-c.png)
![](https://s3.amazonaws.com/johnfurrow.com/share/flood-screenshots-d.png)
![](https://s3.amazonaws.com/johnfurrow.com/share/flood-screenshots-e.png)
![](https://s3.amazonaws.com/johnfurrow.com/share/flood-screenshots-f.png)
Check out [`package.json`](./package.json)'s `dependency` property to see the projects that make this app possible. Flood's client-side build tooling is based on the wonderful [create-react-app](https://github.com/facebookincubator/create-react-app).
13 changes: 13 additions & 0 deletions client/src/javascript/actions/AuthActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ let AuthActions = {
});
},

updateUser: (username, connectionSettings) => {
const requestPayload = {};

if (connectionSettings.connectionType === 'socket') {
requestPayload.socketPath = connectionSettings.rtorrentSocketPath;
} else {
requestPayload.port = connectionSettings.rtorrentPort;
requestPayload.host = connectionSettings.rtorrentHost;
}

return axios.patch(`${baseURI}auth/users/${encodeURIComponent(username)}`, requestPayload).then((json = {}) => json.data);
},

deleteUser: (username) => {
return axios.delete(`${baseURI}auth/users/${encodeURIComponent(username)}`)
.then((json = {}) => json.data)
Expand Down
24 changes: 24 additions & 0 deletions client/src/javascript/actions/ClientActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,30 @@ let ClientActions = {
}
});
});
},

testClientConnectionSettings: (connectionSettings) => {
const requestPayload = {
host: connectionSettings.rtorrentHost,
port: connectionSettings.rtorrentPort,
socketPath: connectionSettings.rtorrentSocketPath
};

return axios.post(`${baseURI}api/client/connection-test`, requestPayload).then((json = {}) => json.data);
},

testConnection: () => {
return axios.get(`${baseURI}api/client/connection-test`)
.then((json = {}) => json.data)
.then((data) => {
AppDispatcher.dispatchServerAction({
type: ActionTypes.CLIENT_CONNECTION_TEST_SUCCESS
});
}, (error) => {
AppDispatcher.dispatchServerAction({
type: ActionTypes.CLIENT_CONNECTION_TEST_ERROR
});
});
}
};

Expand Down
17 changes: 17 additions & 0 deletions client/src/javascript/actions/FloodActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ const FloodActions = {
closeActivityStream() {
activityStreamEventSource.close();

activityStreamEventSource.removeEventListener(
serverEventTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE,
this.handleClientConnectivityStatusChange
);

activityStreamEventSource.removeEventListener(
serverEventTypes.NOTIFICATION_COUNT_CHANGE,
this.handleNotificationCountChange
Expand Down Expand Up @@ -177,6 +182,13 @@ const FloodActions = {
});
},

handleClientConnectivityStatusChange(event) {
AppDispatcher.dispatchServerAction({
type: ActionTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE,
data: JSON.parse(event.data)
});
},

handleNotificationCountChange(event) {
AppDispatcher.dispatchServerAction({
type: ActionTypes.NOTIFICATION_COUNT_CHANGE,
Expand Down Expand Up @@ -260,6 +272,11 @@ const FloodActions = {
`${baseURI}api/activity-stream?historySnapshot=${historySnapshot}`
);

activityStreamEventSource.addEventListener(
serverEventTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE,
this.handleClientConnectivityStatusChange
);

activityStreamEventSource.addEventListener(
serverEventTypes.NOTIFICATION_COUNT_CHANGE,
this.handleNotificationCountChange
Expand Down
27 changes: 10 additions & 17 deletions client/src/javascript/app.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import {FormattedMessage, IntlProvider} from 'react-intl';
import {IntlProvider} from 'react-intl';
import {IndexRoute, Router, Route, browserHistory} from 'react-router';
import React from 'react';
import ReactDOM from 'react-dom';

import * as i18n from './i18n/languages';
import AuthEnforcer from './components/auth/AuthEnforcer';
import AppWrapper from './components/AppWrapper';
import EventTypes from './constants/EventTypes';
import FloodActions from './actions/FloodActions';
import Login from './components/views/Login';
import Register from './components/views/Register';
import SettingsStore from './stores/SettingsStore';
import TorrentClientOverview from './components/views/TorrentClientOverview';
import UIStore from './stores/UIStore';

import '../sass/style.scss';

const appRoutes = (
<Router history={browserHistory}>
<Route path="/" component={AuthEnforcer}>
<Route path="/" component={AppWrapper}>
<IndexRoute component={Login} />
<Route path="login" component={Login} />
<Route path="register" component={Register} />
Expand All @@ -40,22 +39,17 @@ class FloodApp extends React.Component {
this[method] = this[method].bind(this);
});

UIStore.registerDependency({
id: 'flood-settings',
message: (
<FormattedMessage id="dependency.loading.flood.settings"
defaultMessage="Flood Settings" />
)
});

FloodActions.startActivityStream();
}

componentDidMount() {
SettingsStore.listen(
EventTypes.SETTINGS_CHANGE,
this.handleSettingsChange
);
);

SettingsStore.fetchClientSettings();
SettingsStore.fetchFloodSettings();
}

componentWillUnmount() {
Expand All @@ -66,11 +60,10 @@ class FloodApp extends React.Component {
}

handleSettingsChange() {
if (SettingsStore.getFloodSettings('language') !== this.state.language) {
this.setState({locale: SettingsStore.getFloodSettings('language')});
const nextLocale = SettingsStore.getFloodSettings('language');
if (nextLocale !== this.state.language) {
this.setState({locale: nextLocale});
}

UIStore.satisfyDependency('flood-settings');
}

render() {
Expand Down
Loading

0 comments on commit f492ad3

Please sign in to comment.