aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjloup <jloup@jloup.work>2018-05-13 23:14:26 +0200
committerjloup <jloup@jloup.work>2018-05-13 23:18:48 +0200
commit2e4885d98ec49203180deb7e4e9148762e4720e7 (patch)
treef018a158d2f39133a21dba899176f6fde606bd92
parent6bf174a95ba0f71abf25397316fc101405381cdf (diff)
downloadFront-2e4885d98ec49203180deb7e4e9148762e4720e7.tar.gz
Front-2e4885d98ec49203180deb7e4e9148762e4720e7.tar.zst
Front-2e4885d98ec49203180deb7e4e9148762e4720e7.zip
Admin minimal dashboard.v0.0.14
-rw-r--r--api/admin.go46
-rw-r--r--api/routes.go12
-rw-r--r--api/user.go7
-rw-r--r--cmd/app/main.go1
-rw-r--r--cmd/web/js/admin.jsx45
-rw-r--r--cmd/web/js/api.js9
-rw-r--r--cmd/web/js/balance.jsx14
-rw-r--r--cmd/web/js/main.jsx7
-rw-r--r--db/market_config.go14
9 files changed, 150 insertions, 5 deletions
diff --git a/api/admin.go b/api/admin.go
new file mode 100644
index 0000000..0ac6050
--- /dev/null
+++ b/api/admin.go
@@ -0,0 +1,46 @@
1package api
2
3import (
4 "fmt"
5
6 "immae.eu/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front/db"
7)
8
9type GetAllPortfoliosQuery struct {
10 In struct {
11 Market string
12 }
13 Out map[string]Portfolio
14}
15
16func (q GetAllPortfoliosQuery) ValidateParams() *Error {
17 if q.In.Market != "poloniex" {
18 return &Error{BadRequest, "invalid market name", fmt.Errorf("'%v' is not a valid market name", q.In.Market)}
19 }
20
21 return nil
22}
23
24func (q GetAllPortfoliosQuery) Run() (interface{}, *Error) {
25 u, err := db.GetActiveUsers()
26 if err != nil {
27 return nil, NewInternalError(err)
28 }
29
30 q.Out = make(map[string]Portfolio)
31
32 for _, marketConfig := range u {
33 report, err := GetWeekPortfolio(marketConfig)
34 if ErrorIs(err, NotFound) {
35 continue
36 }
37
38 if err != nil {
39 return nil, NewInternalError(err)
40 }
41
42 q.Out[marketConfig.User.Email] = report.Round()
43 }
44
45 return q.Out, nil
46}
diff --git a/api/routes.go b/api/routes.go
index 3adbfe9..a102419 100644
--- a/api/routes.go
+++ b/api/routes.go
@@ -58,7 +58,9 @@ var Groups = []Group{
58 { 58 {
59 "/admin", 59 "/admin",
60 []Middleware{JwtAuth, UserConfirmed, UserIsAdmin, OtpAuth}, 60 []Middleware{JwtAuth, UserConfirmed, UserIsAdmin, OtpAuth},
61 []Route{}, 61 []Route{
62 {"GET", []gin.HandlerFunc{AdminGetAllPortfolios}, "/portfolios"},
63 },
62 }, 64 },
63} 65}
64 66
@@ -189,3 +191,11 @@ func UserAccount(c *gin.Context) {
189 191
190 RunQuery(query, c) 192 RunQuery(query, c)
191} 193}
194
195func AdminGetAllPortfolios(c *gin.Context) {
196 query := &GetAllPortfoliosQuery{}
197
198 query.In.Market = "poloniex"
199
200 RunQuery(query, c)
201}
diff --git a/api/user.go b/api/user.go
index bc24bbb..ff539f0 100644
--- a/api/user.go
+++ b/api/user.go
@@ -62,7 +62,8 @@ type SignParams struct {
62} 62}
63 63
64type SignResult struct { 64type SignResult struct {
65 Token string `json:"token"` 65 Token string `json:"token"`
66 IsAdmin bool `json:"isAdmin"`
66} 67}
67 68
68func (s SignParams) Validate() *Error { 69func (s SignParams) Validate() *Error {
@@ -142,7 +143,7 @@ func (q SignupQuery) Run() (interface{}, *Error) {
142 } 143 }
143 } 144 }
144 145
145 return SignResult{token}, nil 146 return SignResult{token, newUser.Role == db.RoleAdmin}, nil
146} 147}
147 148
148type SigninQuery struct { 149type SigninQuery struct {
@@ -173,7 +174,7 @@ func (q SigninQuery) Run() (interface{}, *Error) {
173 return nil, NewInternalError(err) 174 return nil, NewInternalError(err)
174 } 175 }
175 176
176 return SignResult{token}, nil 177 return SignResult{token, user.Role == db.RoleAdmin}, nil
177} 178}
178 179
179type ConfirmEmailQuery struct { 180type ConfirmEmailQuery struct {
diff --git a/cmd/app/main.go b/cmd/app/main.go
index 2cf29f9..4405bf1 100644
--- a/cmd/app/main.go
+++ b/cmd/app/main.go
@@ -146,6 +146,7 @@ func main() {
146 "/change-password", 146 "/change-password",
147 "/signout", 147 "/signout",
148 "/me", 148 "/me",
149 "/admin",
149 "/account", 150 "/account",
150 "/otp/enroll", 151 "/otp/enroll",
151 "/otp/validate", 152 "/otp/validate",
diff --git a/cmd/web/js/admin.jsx b/cmd/web/js/admin.jsx
new file mode 100644
index 0000000..81ce15d
--- /dev/null
+++ b/cmd/web/js/admin.jsx
@@ -0,0 +1,45 @@
1import Api from './api.js';
2import React from 'react';
3import {PFBalanceMinimal} from './balance.js';
4import Panel from './panel.js';
5
6class AdminDashboard extends React.Component {
7 constructor(state) {
8 super(state);
9 this.state = {'portfolios': null};
10 }
11
12 load = () => {
13 Api.Call('ADMIN_PORTFOLIOS', {}, function(err, status, data) {
14 if (err) {
15 console.error(err, data);
16 return;
17 }
18
19 this.setState({'portfolios': data});
20 }.bind(this));
21 }
22
23 componentDidMount = () => {
24 this.load();
25 }
26
27 render = () => {
28 if (this.state.portfolios === null) {
29 return <div></div>;
30 }
31 var portfolios = Object.keys(this.state.portfolios).map(function(email) {
32 return <div className="row" key={email}>
33 <div className="col-6"><span>{email}:</span></div>
34 <div className="col-6 text-center">
35 <PFBalanceMinimal variationP={this.state.portfolios[email].performance.variationP} balance={this.state.portfolios[email].value} periodStart={this.state.portfolios[email].periodStart}/>
36 </div>
37 </div>;
38 }.bind(this));
39
40 return <Panel component={<div>{portfolios}</div>} title="Portfolios Overview"/>;
41 }
42
43}
44
45export default AdminDashboard;
diff --git a/cmd/web/js/api.js b/cmd/web/js/api.js
index 63355f0..b626c5f 100644
--- a/cmd/web/js/api.js
+++ b/cmd/web/js/api.js
@@ -142,6 +142,15 @@ var ApiEndpoints = {
142 return '/otp/validate'; 142 return '/otp/validate';
143 } 143 }
144 }, 144 },
145 'ADMIN_PORTFOLIOS': {
146 'type': 'GET',
147 'auth': true,
148 'parameters': [],
149 'buildUrl': function() {
150 return '/admin/portfolios';
151 }
152 },
153
145}; 154};
146 155
147Api.BuildRequest = function(endpointId, params) { 156Api.BuildRequest = function(endpointId, params) {
diff --git a/cmd/web/js/balance.jsx b/cmd/web/js/balance.jsx
index 515a7ed..6217e96 100644
--- a/cmd/web/js/balance.jsx
+++ b/cmd/web/js/balance.jsx
@@ -95,4 +95,16 @@ class PFBalance extends React.Component {
95 } 95 }
96} 96}
97 97
98export {PFBalance, Assets}; 98class PFBalanceMinimal extends React.Component {
99 render = () => {
100 return <React.Fragment>
101 <div className="balance">
102 <div className="col-12">
103 <CurrencyLogo currency="BTC" /> <span><strong>{this.props.balance}</strong> <strong>{formatVariation(this.props.variationP)}</strong></span>
104 </div>
105 </div>
106 </React.Fragment>;
107 }
108}
109
110export {PFBalance, Assets, PFBalanceMinimal};
diff --git a/cmd/web/js/main.jsx b/cmd/web/js/main.jsx
index 7fa2d26..8014377 100644
--- a/cmd/web/js/main.jsx
+++ b/cmd/web/js/main.jsx
@@ -5,6 +5,7 @@ import ChangePasswordForm from './change_password.js';
5import OtpEnrollForm from './otp.js'; 5import OtpEnrollForm from './otp.js';
6import PoloniexController from './poloniex.js'; 6import PoloniexController from './poloniex.js';
7import UserAccount from './account.js'; 7import UserAccount from './account.js';
8import AdminDashboard from './admin.js';
8import App from './app.js'; 9import App from './app.js';
9import Api from './api.js'; 10import Api from './api.js';
10import cookies from './cookies.js'; 11import cookies from './cookies.js';
@@ -101,6 +102,12 @@ App.page('/account', true, function(context) {
101 </div>); 102 </div>);
102}); 103});
103 104
105App.page('/admin', true, function(context) {
106 App.mount(<div>
107 <AdminDashboard/>
108 </div>);
109});
110
104App.page('/not_confirmed', true, function(context) { 111App.page('/not_confirmed', true, function(context) {
105 App.mount(<div> 112 App.mount(<div>
106 <div className="row"> 113 <div className="row">
diff --git a/db/market_config.go b/db/market_config.go
index 30b4538..afb2842 100644
--- a/db/market_config.go
+++ b/db/market_config.go
@@ -16,6 +16,9 @@ type MarketConfig struct {
16 UserId int64 16 UserId int64
17 Status MarketConfigStatus 17 Status MarketConfigStatus
18 Config map[string]string 18 Config map[string]string
19
20 // Will be expanded by pg
21 User User
19} 22}
20 23
21func InsertMarketConfig(config *MarketConfig) error { 24func InsertMarketConfig(config *MarketConfig) error {
@@ -62,3 +65,14 @@ func SetMarketConfigStatus(marketConfig MarketConfig, status MarketConfigStatus)
62 65
63 return &marketConfig, err 66 return &marketConfig, err
64} 67}
68
69func GetActiveUsers() ([]MarketConfig, error) {
70 var configs []MarketConfig
71
72 err := DB.Model(&configs).Column("User").Where("market_config.status=?", "enabled").Select()
73 if err != nil {
74 return nil, err
75 }
76
77 return configs, nil
78}