diff options
author | jloup <jloup@jloup.work> | 2018-05-13 23:14:26 +0200 |
---|---|---|
committer | jloup <jloup@jloup.work> | 2018-05-13 23:18:48 +0200 |
commit | 2e4885d98ec49203180deb7e4e9148762e4720e7 (patch) | |
tree | f018a158d2f39133a21dba899176f6fde606bd92 | |
parent | 6bf174a95ba0f71abf25397316fc101405381cdf (diff) | |
download | Front-2e4885d98ec49203180deb7e4e9148762e4720e7.tar.gz Front-2e4885d98ec49203180deb7e4e9148762e4720e7.tar.zst Front-2e4885d98ec49203180deb7e4e9148762e4720e7.zip |
Admin minimal dashboard.v0.0.14
-rw-r--r-- | api/admin.go | 46 | ||||
-rw-r--r-- | api/routes.go | 12 | ||||
-rw-r--r-- | api/user.go | 7 | ||||
-rw-r--r-- | cmd/app/main.go | 1 | ||||
-rw-r--r-- | cmd/web/js/admin.jsx | 45 | ||||
-rw-r--r-- | cmd/web/js/api.js | 9 | ||||
-rw-r--r-- | cmd/web/js/balance.jsx | 14 | ||||
-rw-r--r-- | cmd/web/js/main.jsx | 7 | ||||
-rw-r--r-- | db/market_config.go | 14 |
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 @@ | |||
1 | package api | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "immae.eu/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front/db" | ||
7 | ) | ||
8 | |||
9 | type GetAllPortfoliosQuery struct { | ||
10 | In struct { | ||
11 | Market string | ||
12 | } | ||
13 | Out map[string]Portfolio | ||
14 | } | ||
15 | |||
16 | func (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 | |||
24 | func (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 | |||
195 | func 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 | ||
64 | type SignResult struct { | 64 | type SignResult struct { |
65 | Token string `json:"token"` | 65 | Token string `json:"token"` |
66 | IsAdmin bool `json:"isAdmin"` | ||
66 | } | 67 | } |
67 | 68 | ||
68 | func (s SignParams) Validate() *Error { | 69 | func (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 | ||
148 | type SigninQuery struct { | 149 | type 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 | ||
179 | type ConfirmEmailQuery struct { | 180 | type 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 @@ | |||
1 | import Api from './api.js'; | ||
2 | import React from 'react'; | ||
3 | import {PFBalanceMinimal} from './balance.js'; | ||
4 | import Panel from './panel.js'; | ||
5 | |||
6 | class 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 | |||
45 | export 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 | ||
147 | Api.BuildRequest = function(endpointId, params) { | 156 | Api.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 | ||
98 | export {PFBalance, Assets}; | 98 | class 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 | |||
110 | export {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'; | |||
5 | import OtpEnrollForm from './otp.js'; | 5 | import OtpEnrollForm from './otp.js'; |
6 | import PoloniexController from './poloniex.js'; | 6 | import PoloniexController from './poloniex.js'; |
7 | import UserAccount from './account.js'; | 7 | import UserAccount from './account.js'; |
8 | import AdminDashboard from './admin.js'; | ||
8 | import App from './app.js'; | 9 | import App from './app.js'; |
9 | import Api from './api.js'; | 10 | import Api from './api.js'; |
10 | import cookies from './cookies.js'; | 11 | import cookies from './cookies.js'; |
@@ -101,6 +102,12 @@ App.page('/account', true, function(context) { | |||
101 | </div>); | 102 | </div>); |
102 | }); | 103 | }); |
103 | 104 | ||
105 | App.page('/admin', true, function(context) { | ||
106 | App.mount(<div> | ||
107 | <AdminDashboard/> | ||
108 | </div>); | ||
109 | }); | ||
110 | |||
104 | App.page('/not_confirmed', true, function(context) { | 111 | App.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 | ||
21 | func InsertMarketConfig(config *MarketConfig) error { | 24 | func 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 | |||
69 | func 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 | } | ||