Merge branch 'admin' into 'master'

Admin panel

See merge request !86
This commit is contained in:
Lucas 2017-08-11 08:52:42 +00:00
commit f6ad0dc85e
10 changed files with 161 additions and 3 deletions

View File

@ -0,0 +1,26 @@
package admin
import (
"net/http"
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
"github.com/sirupsen/logrus"
)
// GetUsersHandler returns the user list
func GetUsersHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
log := env.Log.WithFields(logrus.Fields{
"function": "admin.GetUsersHandler",
})
log.Debug("Getting users")
users, err := users.GetAll(env.Database)
if err != nil {
return env.RenderError(w, err)
}
return env.RenderJSON(w, users)
}

View File

@ -24,6 +24,8 @@ const (
getTokensQuery = `SELECT id, value FROM tokens WHERE user_id=$1;`
checkTokenQuery = `SELECT count(*) FROM tokens WHERE user_id=$1 AND value=$2;`
deleteTokenQuery = `DELETE FROM tokens WHERE user_id=$1 AND value=$2;`
getAllUsersQuery = `SELECT * FROM users;`
)
const (
@ -133,6 +135,16 @@ func Get(q sqlx.Queryer, name string) (*User, error) {
return u, nil
}
// GetAll returns all the users
func GetAll(db *sqlx.DB) ([]*User, error) {
users := []*User{}
err := db.Select(&users, getAllUsersQuery)
if err != nil {
return nil, err
}
return users, nil
}
// Add user to database or raises an error
func (u *User) Add(q sqlx.Queryer) error {
var id string

View File

@ -0,0 +1,8 @@
import { configureAxios, request } from "../requests"
export function getUsers() {
return request(
"ADMIN_LIST_USERS",
configureAxios().get("/admins/users")
)
}

View File

@ -46,6 +46,7 @@ function mapStateToProps(state) {
}
return {
username: state.userStore.get("username"),
isAdmin: state.userStore.get("isAdmin"),
torrentCount: torrentCount,
alerts: state.alerts,
}
@ -60,6 +61,7 @@ function Main(props) {
<div>
<NavBar
username={props.username}
isAdmin={props.isAdmin}
router={props.router}
torrentCount={props.torrentCount}
/>

View File

@ -0,0 +1,64 @@
import React from "react"
import { connect } from "react-redux"
import { bindActionCreators } from "redux"
import { getUsers } from "../../actions/admins"
function mapStateToProps(state) {
return {
users : state.adminStore.get("users"),
};
}
const mapDispatchToProps = (dipatch) =>
bindActionCreators({ getUsers }, dipatch)
function AdminView(props) {
return (
<UserList users={props.users} />
);
}
function UserList(props) {
return (
<div>
<h2 className="hidden-xs">Users</h2>
<h3 className="visible-xs">Users</h3>
<table className="table">
<thead>
<tr className="active">
<th>#</th>
<th>Name</th>
<th>Admin</th>
<th>Polochon URL</th>
<th>Polochon token</th>
</tr>
</thead>
<tbody>
{props.users.map(function(el, index) {
return (
<User key={index} data={el} />
);
})}
</tbody>
</table>
</div>
);
}
function User(props) {
const polochonConfig = props.data.get("RawConfig").get("polochon");
const polochonURL = polochonConfig ? polochonConfig.get("url") : "-";
const polochonToken = polochonConfig ? polochonConfig.get("token") : "-";
const admin = props.data.get("Admin") ? "yes" : "no";
return (
<tr>
<td>{props.data.get("id")}</td>
<td>{props.data.get("Name")}</td>
<td>{admin}</td>
<td>{polochonURL}</td>
<td>{polochonToken}</td>
<td></td>
</tr>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(AdminView);

View File

@ -63,7 +63,7 @@ export default class AppNavBar extends React.PureComponent {
{this.state.userLoggedIn &&
<Torrents torrentsCount={this.props.torrentCount} />
}
<UserDropdown username={this.props.username} />
<UserDropdown username={this.props.username} isAdmin={this.props.isAdmin} />
{(this.state.displayMoviesSearch && this.state.userLoggedIn) &&
<Search
placeholder="Search movies"
@ -147,6 +147,11 @@ function UserDropdown(props) {
return (
<Nav pullRight>
<NavDropdown title={props.username} id="navbar-dropdown-right">
{props.isAdmin &&
<LinkContainer to="/admin">
<MenuItem>Admin Panel</MenuItem>
</LinkContainer>
}
<LinkContainer to="/users/edit">
<MenuItem>Edit</MenuItem>
</LinkContainer>

View File

@ -0,0 +1,12 @@
import { Map, List, fromJS } from "immutable"
const defaultState = Map({
"users": List(),
});
const handlers = {
"ADMIN_LIST_USERS_FULFILLED": (state, action) => state.set("users", fromJS(action.payload.response.data)),
}
export default (state = defaultState, action) =>
handlers[action.type] ? handlers[action.type](state, action) : state;

View File

@ -7,6 +7,7 @@ import showStore from "./show"
import userStore from "./users"
import alerts from "./alerts"
import torrentStore from "./torrents"
import adminStore from "./admins"
const rootReducer = combineReducers({
routing: routerReducer,
@ -16,6 +17,7 @@ const rootReducer = combineReducers({
userStore,
alerts,
torrentStore,
adminStore,
})
export default rootReducer;

View File

@ -5,18 +5,23 @@ import UserLoginForm from "./components/users/login"
import UserEdit from "./components/users/edit"
import UserSignUp from "./components/users/signup"
import TorrentList from "./components/torrents/list"
import AdminView from "./components/admins/users"
import { fetchTorrents } from "./actions/torrents"
import { userLogout, getUserInfos } from "./actions/users"
import { fetchMovies, getMovieExploreOptions } from "./actions/movies"
import { fetchShows, fetchShowDetails, getShowExploreOptions } from "./actions/shows"
import { getUsers } from "./actions/admins"
import store from "./store"
// Default route
const defaultRoute = "/movies/explore/yts/seeds";
// This function returns true if the user is logged in, false otherwise
function isLoggedIn() {
const state = store.getState();
const isLogged = state.userStore.isLogged;
const isLogged = state.userStore.get("isLogged");
let token = localStorage.getItem("token");
// Let's check if the user has a token, if he does let's assume he's logged
@ -59,7 +64,16 @@ const loginCheck = function(nextState, replace, next, f = null) {
next();
}
const defaultRoute = "/movies/explore/yts/seeds";
const adminCheck = function(nextState, replace, next, f = null) {
const state = store.getState();
const isAdmin = state.userStore.get("isAdmin");
loginCheck(nextState, replace, next, function() {
if (!isAdmin) { replace(defaultRoute); }
if (f) { f(); }
next();
})
}
export default function getRoutes(App) {
return {
path: "/",
@ -207,6 +221,15 @@ export default function getRoutes(App) {
});
},
},
{
path: "/admin",
component: AdminView,
onEnter: function(nextState, replace, next) {
adminCheck(nextState, replace, next, function() {
store.dispatch(getUsers());
});
},
},
],
};
};

View File

@ -1,6 +1,7 @@
package main
import (
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/admins"
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/external_medias"
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/movies"
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/shows"
@ -56,4 +57,7 @@ func setupRoutes(env *web.Env) {
// Route to refresh all movies and shows
env.Handle("/refresh", extmedias.RefreshHandler).WithRole(users.AdminRole).Methods("POST")
// Admin routes
env.Handle("/admins/users", admin.GetUsersHandler).WithRole(users.AdminRole).Methods("GET")
}