Add a page to list / delete the user tokens
This commit is contained in:
parent
bd20a87548
commit
2ddca462da
@ -50,3 +50,20 @@ export function getUserInfos() {
|
|||||||
configureAxios().get("/users/details")
|
configureAxios().get("/users/details")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getUserTokens() {
|
||||||
|
return request(
|
||||||
|
"GET_USER_TOKENS",
|
||||||
|
configureAxios().get("/users/tokens")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteUserToken(token) {
|
||||||
|
return request(
|
||||||
|
"DELETE_USER_TOKEN",
|
||||||
|
configureAxios().delete(`/users/tokens/${token}`),
|
||||||
|
[
|
||||||
|
() => getUserTokens(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -164,6 +164,11 @@ function UserDropdown(props) {
|
|||||||
<MenuItem>Edit</MenuItem>
|
<MenuItem>Edit</MenuItem>
|
||||||
</LinkContainer>
|
</LinkContainer>
|
||||||
}
|
}
|
||||||
|
{props.isActivated &&
|
||||||
|
<LinkContainer to="/users/tokens">
|
||||||
|
<MenuItem>Tokens</MenuItem>
|
||||||
|
</LinkContainer>
|
||||||
|
}
|
||||||
<LinkContainer to="/users/logout">
|
<LinkContainer to="/users/logout">
|
||||||
<MenuItem>Logout</MenuItem>
|
<MenuItem>Logout</MenuItem>
|
||||||
</LinkContainer>
|
</LinkContainer>
|
||||||
|
187
frontend/js/components/users/tokens.js
Normal file
187
frontend/js/components/users/tokens.js
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import React from "react"
|
||||||
|
import { connect } from "react-redux"
|
||||||
|
import { UAParser } from "ua-parser-js"
|
||||||
|
import moment from "moment"
|
||||||
|
|
||||||
|
import { bindActionCreators } from "redux"
|
||||||
|
import { deleteUserToken } from "../../actions/users"
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) =>
|
||||||
|
bindActionCreators({ deleteUserToken }, dispatch)
|
||||||
|
|
||||||
|
function mapStateToProps(state) {
|
||||||
|
return {
|
||||||
|
tokens: state.userStore.get("tokens"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function UserTokens(props) {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
<div className="content-fluid">
|
||||||
|
<TokenList {...props} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TokenList(props) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2 className="hidden-xs">Tokens</h2>
|
||||||
|
<h3 className="visible-xs">Tokens</h3>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{props.tokens.map(function(el, index) {
|
||||||
|
return (
|
||||||
|
<Token
|
||||||
|
key={index}
|
||||||
|
data={el}
|
||||||
|
deleteToken={props.deleteUserToken}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Token(props) {
|
||||||
|
const ua = UAParser(props.data.get("user_agent"));
|
||||||
|
return (
|
||||||
|
<div className="panel panel-default">
|
||||||
|
<div className="container">
|
||||||
|
<Logo {...ua} />
|
||||||
|
<div className="col-xs-10">
|
||||||
|
<dl className="dl-horizontal">
|
||||||
|
<dt>Description</dt>
|
||||||
|
<dd>{props.data.get("description")}</dd>
|
||||||
|
|
||||||
|
<dt>Last IP</dt>
|
||||||
|
<dd>{props.data.get("ip")}</dd>
|
||||||
|
|
||||||
|
<dt>Last used</dt>
|
||||||
|
<dd>{moment(props.data.get("last_used")).fromNow()}</dd>
|
||||||
|
|
||||||
|
<dt>Created</dt>
|
||||||
|
<dd>{moment(props.data.get("created_at")).fromNow()}</dd>
|
||||||
|
|
||||||
|
<dt>Device</dt>
|
||||||
|
<dd><Device {...ua.device}/></dd>
|
||||||
|
|
||||||
|
<dt>OS</dt>
|
||||||
|
<dd><OS {...ua.os}/></dd>
|
||||||
|
|
||||||
|
<dt>Browser</dt>
|
||||||
|
<dd><Browser {...ua.browser}/></dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<Actions {...props} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Actions extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
handleClick() {
|
||||||
|
const token = this.props.data.get("token");
|
||||||
|
this.props.deleteToken(token);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="col-xs-1">
|
||||||
|
<span
|
||||||
|
className="fa fa-trash fa-lg pull-right clickable user-token-action"
|
||||||
|
onClick={this.handleClick}>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Logo(props) {
|
||||||
|
var className;
|
||||||
|
if (props.ua === "canape-cli") {
|
||||||
|
className = "terminal";
|
||||||
|
} else if (props.device.type == "mobile" ){
|
||||||
|
className = "mobile";
|
||||||
|
} else {
|
||||||
|
switch (props.browser.name) {
|
||||||
|
case "Chrome":
|
||||||
|
case "chrome":
|
||||||
|
className = "chrome";
|
||||||
|
break;
|
||||||
|
case "Safari":
|
||||||
|
case "safari":
|
||||||
|
className = "safari";
|
||||||
|
break;
|
||||||
|
case "Firefox":
|
||||||
|
case "firefox":
|
||||||
|
className = "firefox";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
className = "question";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="user-token-icon">
|
||||||
|
<div className="hidden-xs hidden-sm col-md-1">
|
||||||
|
<span className={"fa fa-" + className + " fa-5x"}></span>
|
||||||
|
</div>
|
||||||
|
<div className="hidden-md hidden-lg col-xs-1">
|
||||||
|
<span className={"fa fa-" + className + " fa-lg"}></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function OS(props) {
|
||||||
|
var osName = "-";
|
||||||
|
|
||||||
|
if (props.name !== undefined) {
|
||||||
|
osName = props.name;
|
||||||
|
|
||||||
|
if (props.version !== undefined) {
|
||||||
|
osName += " " + props.version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span> {osName}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Device(props) {
|
||||||
|
var deviceName = "-";
|
||||||
|
|
||||||
|
if (props.model !== undefined) {
|
||||||
|
deviceName = props.model;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span> {deviceName}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Browser(props) {
|
||||||
|
var browserName = "-";
|
||||||
|
if (props.name !== undefined) {
|
||||||
|
browserName = props.name;
|
||||||
|
|
||||||
|
if (props.version !== undefined) {
|
||||||
|
browserName += " - " + props.version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span> {browserName}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(UserTokens);
|
@ -1,4 +1,4 @@
|
|||||||
import { Map } from "immutable"
|
import { Map, List, fromJS } from "immutable"
|
||||||
|
|
||||||
import jwtDecode from "jwt-decode"
|
import jwtDecode from "jwt-decode"
|
||||||
import Cookies from "universal-cookie"
|
import Cookies from "universal-cookie"
|
||||||
@ -11,6 +11,7 @@ const defaultState = Map({
|
|||||||
isLogged: false,
|
isLogged: false,
|
||||||
polochonToken: "",
|
polochonToken: "",
|
||||||
polochonUrl: "",
|
polochonUrl: "",
|
||||||
|
tokens: List(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
@ -27,6 +28,9 @@ const handlers = {
|
|||||||
polochonToken: action.payload.response.data.token,
|
polochonToken: action.payload.response.data.token,
|
||||||
polochonUrl: action.payload.response.data.url,
|
polochonUrl: action.payload.response.data.url,
|
||||||
})),
|
})),
|
||||||
|
"GET_USER_TOKENS_FULFILLED": (state, action) => state.set(
|
||||||
|
"tokens", fromJS(action.payload.response.data)
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
function logoutUser() {
|
function logoutUser() {
|
||||||
|
@ -3,6 +3,7 @@ import ShowList from "./components/shows/list"
|
|||||||
import ShowDetails from "./components/shows/details"
|
import ShowDetails from "./components/shows/details"
|
||||||
import UserLoginForm from "./components/users/login"
|
import UserLoginForm from "./components/users/login"
|
||||||
import UserEdit from "./components/users/edit"
|
import UserEdit from "./components/users/edit"
|
||||||
|
import UserTokens from "./components/users/tokens"
|
||||||
import UserActivation from "./components/users/activation"
|
import UserActivation from "./components/users/activation"
|
||||||
import UserSignUp from "./components/users/signup"
|
import UserSignUp from "./components/users/signup"
|
||||||
import TorrentList from "./components/torrents/list"
|
import TorrentList from "./components/torrents/list"
|
||||||
@ -10,7 +11,7 @@ import TorrentSearch from "./components/torrents/search"
|
|||||||
import AdminPanel from "./components/admins/panel"
|
import AdminPanel from "./components/admins/panel"
|
||||||
|
|
||||||
import { fetchTorrents, searchTorrents } from "./actions/torrents"
|
import { fetchTorrents, searchTorrents } from "./actions/torrents"
|
||||||
import { userLogout, getUserInfos } from "./actions/users"
|
import { userLogout, getUserInfos, getUserTokens } from "./actions/users"
|
||||||
import { fetchMovies, getMovieExploreOptions } from "./actions/movies"
|
import { fetchMovies, getMovieExploreOptions } from "./actions/movies"
|
||||||
import { fetchShows, fetchShowDetails, getShowExploreOptions } from "./actions/shows"
|
import { fetchShows, fetchShowDetails, getShowExploreOptions } from "./actions/shows"
|
||||||
import { getUsers, getStats } from "./actions/admins"
|
import { getUsers, getStats } from "./actions/admins"
|
||||||
@ -118,6 +119,15 @@ export default function getRoutes(App) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/users/tokens",
|
||||||
|
component: UserTokens,
|
||||||
|
onEnter: function(nextState, replace, next) {
|
||||||
|
loginCheck(nextState, replace, next, function() {
|
||||||
|
store.dispatch(getUserTokens());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/users/activation",
|
path: "/users/activation",
|
||||||
component: UserActivation,
|
component: UserActivation,
|
||||||
|
@ -53,6 +53,7 @@ body {
|
|||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spaced-icons,
|
||||||
.episode-buttons {
|
.episode-buttons {
|
||||||
div, span {
|
div, span {
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
@ -103,3 +104,11 @@ table.torrent-search-result {
|
|||||||
table.table-align-middle > tbody > tr > td {
|
table.table-align-middle > tbody > tr > td {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.user-token-icon > div {
|
||||||
|
padding-top: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.user-token-action {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"immutable": "^3.8.1",
|
"immutable": "^3.8.1",
|
||||||
"jquery": "^2.2.4",
|
"jquery": "^2.2.4",
|
||||||
"jwt-decode": "^2.1.0",
|
"jwt-decode": "^2.1.0",
|
||||||
|
"moment": "^2.20.1",
|
||||||
"react": "^16.2.0",
|
"react": "^16.2.0",
|
||||||
"react-bootstrap": "^0.32.1",
|
"react-bootstrap": "^0.32.1",
|
||||||
"react-bootstrap-sweetalert": "^4.2.3",
|
"react-bootstrap-sweetalert": "^4.2.3",
|
||||||
@ -31,6 +32,7 @@
|
|||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
|
"ua-parser-js": "^0.7.17",
|
||||||
"universal-cookie": "^2.1.2",
|
"universal-cookie": "^2.1.2",
|
||||||
"webpack": "^3.11.0"
|
"webpack": "^3.11.0"
|
||||||
},
|
},
|
||||||
|
@ -2975,6 +2975,10 @@ minimist@^1.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "0.0.8"
|
minimist "0.0.8"
|
||||||
|
|
||||||
|
moment@^2.20.1:
|
||||||
|
version "2.20.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd"
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
@ -4399,7 +4403,7 @@ typedarray@^0.0.6:
|
|||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
|
|
||||||
ua-parser-js@^0.7.9:
|
ua-parser-js@^0.7.17, ua-parser-js@^0.7.9:
|
||||||
version "0.7.17"
|
version "0.7.17"
|
||||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
|
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user