binwiederhier пре 3 година
родитељ
комит
e650f813c5
7 измењених фајлова са 43 додато и 14 уклоњено
  1. 8 5
      server/server_account.go
  2. 3 0
      server/types.go
  3. 16 4
      server/visitor.go
  4. 6 4
      user/manager.go
  5. 1 0
      user/types.go
  6. 1 0
      web/public/static/langs/en.json
  7. 8 1
      web/src/components/Account.js

+ 8 - 5
server/server_account.go

@@ -39,7 +39,7 @@ func (s *Server) handleAccountCreate(w http.ResponseWriter, r *http.Request, v *
 	return nil
 }
 
-func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *visitor) error {
+func (s *Server) handleAccountGet(w http.ResponseWriter, _ *http.Request, v *visitor) error {
 	stats, err := v.Info()
 	if err != nil {
 		return err
@@ -50,6 +50,8 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *vis
 			MessagesRemaining:            stats.MessagesRemaining,
 			Emails:                       stats.Emails,
 			EmailsRemaining:              stats.EmailsRemaining,
+			Topics:                       stats.Topics,
+			TopicsRemaining:              stats.TopicsRemaining,
 			AttachmentTotalSize:          stats.AttachmentTotalSize,
 			AttachmentTotalSizeRemaining: stats.AttachmentTotalSizeRemaining,
 		},
@@ -57,6 +59,7 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *vis
 			Basis:               stats.Basis,
 			Messages:            stats.MessagesLimit,
 			Emails:              stats.EmailsLimit,
+			Topics:              stats.TopicsLimit,
 			AttachmentTotalSize: stats.AttachmentTotalSizeLimit,
 			AttachmentFileSize:  stats.AttachmentFileSizeLimit,
 		},
@@ -119,7 +122,7 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *vis
 	return nil
 }
 
-func (s *Server) handleAccountDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
+func (s *Server) handleAccountDelete(w http.ResponseWriter, _ *http.Request, v *visitor) error {
 	if err := s.userManager.RemoveUser(v.user.Name); err != nil {
 		return err
 	}
@@ -141,7 +144,7 @@ func (s *Server) handleAccountPasswordChange(w http.ResponseWriter, r *http.Requ
 	return nil
 }
 
-func (s *Server) handleAccountTokenIssue(w http.ResponseWriter, r *http.Request, v *visitor) error {
+func (s *Server) handleAccountTokenIssue(w http.ResponseWriter, _ *http.Request, v *visitor) error {
 	// TODO rate limit
 	token, err := s.userManager.CreateToken(v.user)
 	if err != nil {
@@ -159,7 +162,7 @@ func (s *Server) handleAccountTokenIssue(w http.ResponseWriter, r *http.Request,
 	return nil
 }
 
-func (s *Server) handleAccountTokenExtend(w http.ResponseWriter, r *http.Request, v *visitor) error {
+func (s *Server) handleAccountTokenExtend(w http.ResponseWriter, _ *http.Request, v *visitor) error {
 	// TODO rate limit
 	if v.user == nil {
 		return errHTTPUnauthorized
@@ -182,7 +185,7 @@ func (s *Server) handleAccountTokenExtend(w http.ResponseWriter, r *http.Request
 	return nil
 }
 
-func (s *Server) handleAccountTokenDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
+func (s *Server) handleAccountTokenDelete(w http.ResponseWriter, _ *http.Request, v *visitor) error {
 	// TODO rate limit
 	if v.user.Token == "" {
 		return errHTTPBadRequestNoTokenProvided

+ 3 - 0
server/types.go

@@ -243,6 +243,7 @@ type apiAccountLimits struct {
 	Basis               string `json:"basis"` // "ip", "role" or "plan"
 	Messages            int64  `json:"messages"`
 	Emails              int64  `json:"emails"`
+	Topics              int64  `json:"topics"`
 	AttachmentTotalSize int64  `json:"attachment_total_size"`
 	AttachmentFileSize  int64  `json:"attachment_file_size"`
 }
@@ -252,6 +253,8 @@ type apiAccountStats struct {
 	MessagesRemaining            int64 `json:"messages_remaining"`
 	Emails                       int64 `json:"emails"`
 	EmailsRemaining              int64 `json:"emails_remaining"`
+	Topics                       int64 `json:"topics"`
+	TopicsRemaining              int64 `json:"topics_remaining"`
 	AttachmentTotalSize          int64 `json:"attachment_total_size"`
 	AttachmentTotalSizeRemaining int64 `json:"attachment_total_size_remaining"`
 }

+ 16 - 4
server/visitor.go

@@ -48,6 +48,9 @@ type visitorInfo struct {
 	Emails                       int64
 	EmailsLimit                  int64
 	EmailsRemaining              int64
+	Topics                       int64
+	TopicsLimit                  int64
+	TopicsRemaining              int64
 	AttachmentTotalSize          int64
 	AttachmentTotalSizeLimit     int64
 	AttachmentTotalSizeRemaining int64
@@ -173,20 +176,19 @@ func (v *visitor) Info() (*visitorInfo, error) {
 	info := &visitorInfo{}
 	if v.user != nil && v.user.Role == user.RoleAdmin {
 		info.Basis = "role"
-		info.MessagesLimit = 0
-		info.EmailsLimit = 0
-		info.AttachmentTotalSizeLimit = 0
-		info.AttachmentFileSizeLimit = 0
+		// All limits are zero!
 	} else if v.user != nil && v.user.Plan != nil {
 		info.Basis = "plan"
 		info.MessagesLimit = v.user.Plan.MessagesLimit
 		info.EmailsLimit = v.user.Plan.EmailsLimit
+		info.TopicsLimit = v.user.Plan.TopicsLimit
 		info.AttachmentTotalSizeLimit = v.user.Plan.AttachmentTotalSizeLimit
 		info.AttachmentFileSizeLimit = v.user.Plan.AttachmentFileSizeLimit
 	} else {
 		info.Basis = "ip"
 		info.MessagesLimit = replenishDurationToDailyLimit(v.config.VisitorRequestLimitReplenish)
 		info.EmailsLimit = replenishDurationToDailyLimit(v.config.VisitorEmailLimitReplenish)
+		info.TopicsLimit = 0 // FIXME
 		info.AttachmentTotalSizeLimit = v.config.VisitorAttachmentTotalSizeLimit
 		info.AttachmentFileSizeLimit = v.config.AttachmentFileSizeLimit
 	}
@@ -200,10 +202,20 @@ func (v *visitor) Info() (*visitorInfo, error) {
 	if err != nil {
 		return nil, err
 	}
+	var topics int64
+	if v.user != nil {
+		for _, grant := range v.user.Grants {
+			if grant.Owner {
+				topics++
+			}
+		}
+	}
 	info.Messages = messages
 	info.MessagesRemaining = zeroIfNegative(info.MessagesLimit - info.Messages)
 	info.Emails = emails
 	info.EmailsRemaining = zeroIfNegative(info.EmailsLimit - info.Emails)
+	info.Topics = topics
+	info.TopicsRemaining = zeroIfNegative(info.TopicsLimit - info.Topics)
 	info.AttachmentTotalSize = attachmentsBytesUsed
 	info.AttachmentTotalSizeRemaining = zeroIfNegative(info.AttachmentTotalSizeLimit - info.AttachmentTotalSize)
 	return info, nil

+ 6 - 4
user/manager.go

@@ -35,6 +35,7 @@ const (
 			code TEXT NOT NULL,
 			messages_limit INT NOT NULL,
 			emails_limit INT NOT NULL,
+			topics_limit INT NOT NULL,
 			attachment_file_size_limit INT NOT NULL,
 			attachment_total_size_limit INT NOT NULL,
 			PRIMARY KEY (id)
@@ -75,13 +76,13 @@ const (
 	`
 	createTablesQueries   = `BEGIN; ` + createTablesQueriesNoTx + ` COMMIT;`
 	selectUserByNameQuery = `
-		SELECT u.user, u.pass, u.role, u.messages, u.emails, u.settings, p.code, p.messages_limit, p.emails_limit, p.attachment_file_size_limit, p.attachment_total_size_limit
+		SELECT u.user, u.pass, u.role, u.messages, u.emails, u.settings, p.code, p.messages_limit, p.emails_limit, p.topics_limit, p.attachment_file_size_limit, p.attachment_total_size_limit
 		FROM user u
 		LEFT JOIN plan p on p.id = u.plan_id
 		WHERE user = ?		
 	`
 	selectUserByTokenQuery = `
-		SELECT u.user, u.pass, u.role, u.messages, u.emails, u.settings, p.code, p.messages_limit, p.emails_limit, p.attachment_file_size_limit, p.attachment_total_size_limit
+		SELECT u.user, u.pass, u.role, u.messages, u.emails, u.settings, p.code, p.messages_limit, p.emails_limit, p.topics_limit, p.attachment_file_size_limit, p.attachment_total_size_limit
 		FROM user u
 		JOIN user_token t on u.id = t.user_id
 		LEFT JOIN plan p on p.id = u.plan_id
@@ -469,11 +470,11 @@ func (a *Manager) readUser(rows *sql.Rows) (*User, error) {
 	var username, hash, role string
 	var settings, planCode sql.NullString
 	var messages, emails int64
-	var messagesLimit, emailsLimit, attachmentFileSizeLimit, attachmentTotalSizeLimit sql.NullInt64
+	var messagesLimit, emailsLimit, topicsLimit, attachmentFileSizeLimit, attachmentTotalSizeLimit sql.NullInt64
 	if !rows.Next() {
 		return nil, ErrNotFound
 	}
-	if err := rows.Scan(&username, &hash, &role, &messages, &emails, &settings, &planCode, &messagesLimit, &emailsLimit, &attachmentFileSizeLimit, &attachmentTotalSizeLimit); err != nil {
+	if err := rows.Scan(&username, &hash, &role, &messages, &emails, &settings, &planCode, &messagesLimit, &emailsLimit, &topicsLimit, &attachmentFileSizeLimit, &attachmentTotalSizeLimit); err != nil {
 		return nil, err
 	} else if err := rows.Err(); err != nil {
 		return nil, err
@@ -504,6 +505,7 @@ func (a *Manager) readUser(rows *sql.Rows) (*User, error) {
 			Upgradable:               true, // FIXME
 			MessagesLimit:            messagesLimit.Int64,
 			EmailsLimit:              emailsLimit.Int64,
+			TopicsLimit:              topicsLimit.Int64,
 			AttachmentFileSizeLimit:  attachmentFileSizeLimit.Int64,
 			AttachmentTotalSizeLimit: attachmentTotalSizeLimit.Int64,
 		}

+ 1 - 0
user/types.go

@@ -60,6 +60,7 @@ type Plan struct {
 	Upgradable               bool   `json:"upgradable"`
 	MessagesLimit            int64  `json:"messages_limit"`
 	EmailsLimit              int64  `json:"emails_limit"`
+	TopicsLimit              int64  `json:"topics_limit"`
 	AttachmentFileSizeLimit  int64  `json:"attachment_file_size_limit"`
 	AttachmentTotalSizeLimit int64  `json:"attachment_total_size_limit"`
 }

+ 1 - 0
web/public/static/langs/en.json

@@ -183,6 +183,7 @@
   "account_usage_plan_code_business_plus": "Business Plus",
   "account_usage_messages_title": "Published messages",
   "account_usage_emails_title": "Emails sent",
+  "account_usage_topics_title": "Topics reserved",
   "account_usage_attachment_storage_title": "Attachment storage",
   "account_usage_attachment_storage_subtitle": "{{filesize}} per file",
   "account_usage_basis_ip_description": "Usage stats and limits for this account are based on your IP address, so they may be shared with other users.",

+ 8 - 1
web/src/components/Account.js

@@ -168,7 +168,7 @@ const Stats = () => {
         return <></>;
     }
     const planCode = account.plan.code ?? "none";
-    const normalize = (value, max) => (value / max * 100);
+    const normalize = (value, max) => Math.min(value / max * 100, 100);
     return (
         <Card sx={{p: 3}} aria-label={t("account_usage_title")}>
             <Typography variant="h5" sx={{marginBottom: 2}}>
@@ -182,6 +182,13 @@ const Stats = () => {
                             : t(`account_usage_plan_code_${planCode}`)}
                     </div>
                 </Pref>
+                <Pref title={t("account_usage_topics_title")}>
+                    <div>
+                        <Typography variant="body2" sx={{float: "left"}}>{account.stats.topics}</Typography>
+                        <Typography variant="body2" sx={{float: "right"}}>{account.limits.topics > 0 ? t("account_usage_of_limit", { limit: account.limits.topics }) : t("account_usage_unlimited")}</Typography>
+                    </div>
+                    <LinearProgress variant="determinate" value={account.limits.topics > 0 ? normalize(account.stats.topics, account.limits.topics) : 100} />
+                </Pref>
                 <Pref title={t("account_usage_messages_title")}>
                     <div>
                         <Typography variant="body2" sx={{float: "left"}}>{account.stats.messages}</Typography>