From 06b3fdd17ca4293bc3fd654c2a584a6fd7ae3aff Mon Sep 17 00:00:00 2001 From: Roch D'Amour Date: Sun, 25 Oct 2020 02:21:41 -0400 Subject: [PATCH 1/6] Improved Dockerfile and docker-compose for dev --- Dockerfile | 9 ++++++--- docker-compose.yml | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile index 91e8863..9619b1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,12 @@ # Build Stage FROM golang:1.13 AS build-env -ADD . /src -RUN cd /src && go build -o wakapi +WORKDIR /src +ADD ./go.mod . +RUN go mod download +ADD . . +RUN go build -o wakapi # Final Stage @@ -45,4 +48,4 @@ ADD wait-for-it.sh . VOLUME /data -ENTRYPOINT ./wait-for-it.sh \ No newline at end of file +ENTRYPOINT ./wait-for-it.sh diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..36ce414 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3.7' + +services: + wakapi: + build: . + ports: + - 3000:3000 + restart: always + environment: + WAKAPI_DB_TYPE: "postgres" + WAKAPI_DB_NAME: "wakapi" + WAKAPI_DB_USER: "wakapi" + WAKAPI_DB_PASSWORD: "asdfasdfasdf" + WAKAPI_DB_HOST: "db" + WAKAPI_DB_PORT: "5432" + ENV: "dev" + + db: + image: postgres:12.3 + ports: + - 5432:5432 + environment: + POSTGRES_USER: "wakapi" + POSTGRES_PASSWORD: "asdfasdfasdf" + POSTGRES_DB: "wakapi" From fdf2289f8e1b13b41564f983bcfa7fbdcd32dd54 Mon Sep 17 00:00:00 2001 From: Roch D'Amour Date: Sun, 25 Oct 2020 02:22:10 -0400 Subject: [PATCH 2/6] MVP for custom rules support --- main.go | 10 ++-- migrations/sqlite3/6_customrule_table.sql | 15 ++++++ models/customrule.go | 18 +++++++ models/heartbeat.go | 19 +++---- routes/heartbeat.go | 17 ++++-- routes/settings.go | 63 +++++++++++++++++++++-- services/custom_rule.go | 53 +++++++++++++++++++ services/summary.go | 14 ++--- static/assets/app.js | 4 +- views/settings.tpl.html | 60 +++++++++++++++++++-- wait-for-it.sh | 6 +-- 11 files changed, 240 insertions(+), 39 deletions(-) create mode 100644 migrations/sqlite3/6_customrule_table.sql create mode 100644 models/customrule.go create mode 100644 services/custom_rule.go diff --git a/main.go b/main.go index 1683071..470fb8b 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ var ( aliasService *services.AliasService heartbeatService *services.HeartbeatService userService *services.UserService + customRuleService *services.CustomRuleService summaryService *services.SummaryService aggregationService *services.AggregationService keyValueService *services.KeyValueService @@ -74,7 +75,8 @@ func main() { aliasService = services.NewAliasService(db) heartbeatService = services.NewHeartbeatService(db) userService = services.NewUserService(db) - summaryService = services.NewSummaryService(db, heartbeatService, aliasService) + customRuleService = services.NewCustomRuleService(db) + summaryService = services.NewSummaryService(db, heartbeatService, aliasService, customRuleService) aggregationService = services.NewAggregationService(db, userService, summaryService, heartbeatService) keyValueService = services.NewKeyValueService(db) @@ -88,10 +90,10 @@ func main() { // TODO: move endpoint registration to the respective routes files // Handlers - heartbeatHandler := routes.NewHeartbeatHandler(heartbeatService) summaryHandler := routes.NewSummaryHandler(summaryService) healthHandler := routes.NewHealthHandler(db) - settingsHandler := routes.NewSettingsHandler(userService) + heartbeatHandler := routes.NewHeartbeatHandler(heartbeatService, customRuleService) + settingsHandler := routes.NewSettingsHandler(userService, customRuleService) publicHandler := routes.NewIndexHandler(userService, keyValueService) wakatimeV1AllHandler := wtV1Routes.NewAllTimeHandler(summaryService) wakatimeV1SummariesHandler := wtV1Routes.NewSummariesHandler(summaryService) @@ -136,6 +138,8 @@ func main() { // Settings Routes settingsRouter.Methods(http.MethodGet).HandlerFunc(settingsHandler.GetIndex) settingsRouter.Path("/credentials").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostCredentials) + settingsRouter.Path("/customrules").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostCreateCustomRule) + settingsRouter.Path("/customrules/delete").Methods(http.MethodPost).HandlerFunc(settingsHandler.DeleteCustomRule) settingsRouter.Path("/reset").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostResetApiKey) settingsRouter.Path("/badges").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostToggleBadges) diff --git a/migrations/sqlite3/6_customrule_table.sql b/migrations/sqlite3/6_customrule_table.sql new file mode 100644 index 0000000..6977e6a --- /dev/null +++ b/migrations/sqlite3/6_customrule_table.sql @@ -0,0 +1,15 @@ +-- +migrate Up +-- SQL in section 'Up' is executed when this migration is applied + +create table custom_rules +( + id integer primary key autoincrement, + user_id varchar(255) not null REFERENCES users (id) ON DELETE RESTRICT ON UPDATE RESTRICT, + extension varchar(255), + language varchar(255) +); + +-- +migrate Down +-- SQL section 'Down' is executed when this migration is rolled back + +DROP TABLE custom_rules; diff --git a/models/customrule.go b/models/customrule.go new file mode 100644 index 0000000..c97a633 --- /dev/null +++ b/models/customrule.go @@ -0,0 +1,18 @@ +package models + + +type CustomRule struct { + ID uint `json:"id" gorm:"primary_key"` + User *User `json:"-" gorm:"not null"` + UserID string `json:"-" gorm:"not null; index:idx_customrule_user"` + Extension string `json:"extension"` + Language string `json:"language"` +} + +func validateLanguage(language string) bool { + return len(language) >= 1 +} + +func validateExtension(extension string) bool { + return len(extension) >= 2 +} diff --git a/models/heartbeat.go b/models/heartbeat.go index 8c4088f..bc17624 100644 --- a/models/heartbeat.go +++ b/models/heartbeat.go @@ -1,6 +1,7 @@ package models import ( + "fmt" "regexp" "time" ) @@ -27,19 +28,13 @@ func (h *Heartbeat) Valid() bool { return h.User != nil && h.UserID != "" && h.Time != CustomTime(time.Time{}) } -func (h *Heartbeat) Augment(customLangs map[string]string) { - if h.Language == "" { - if h.languageRegex == nil { - h.languageRegex = regexp.MustCompile(`^.+\.(.+)$`) - } - groups := h.languageRegex.FindAllStringSubmatch(h.Entity, -1) - if len(groups) == 0 || len(groups[0]) != 2 { +func (h *Heartbeat) Augment(customRules []*CustomRule) { + for _, lang := range customRules { + reg := fmt.Sprintf(".*%s$", lang.Extension) + match, err := regexp.MatchString(reg, h.Entity) + if match && err == nil { + h.Language = lang.Language return } - ending := groups[0][1] - if _, ok := customLangs[ending]; !ok { - return - } - h.Language, _ = customLangs[ending] } } diff --git a/routes/heartbeat.go b/routes/heartbeat.go index 5a08cb0..5c52e8e 100644 --- a/routes/heartbeat.go +++ b/routes/heartbeat.go @@ -13,14 +13,16 @@ import ( ) type HeartbeatHandler struct { - config *config2.Config - heartbeatSrvc *services.HeartbeatService + config *config2.Config + heartbeatSrvc *services.HeartbeatService + customRuleSrvc *services.CustomRuleService } -func NewHeartbeatHandler(heartbeatService *services.HeartbeatService) *HeartbeatHandler { +func NewHeartbeatHandler(heartbeatService *services.HeartbeatService, customRuleService *services.CustomRuleService) *HeartbeatHandler { return &HeartbeatHandler{ config: config2.Get(), heartbeatSrvc: heartbeatService, + customRuleSrvc: customRuleService, } } @@ -41,13 +43,20 @@ func (h *HeartbeatHandler) ApiPost(w http.ResponseWriter, r *http.Request) { return } + rules, err := h.customRuleSrvc.GetCustomRuleForUser(user.ID) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + for _, hb := range heartbeats { hb.OperatingSystem = opSys hb.Editor = editor hb.Machine = machineName hb.User = user hb.UserID = user.ID - hb.Augment(h.config.App.CustomLanguages) + hb.Augment(rules) if !hb.Valid() { w.WriteHeader(http.StatusBadRequest) diff --git a/routes/settings.go b/routes/settings.go index 7993e35..cc61d4d 100644 --- a/routes/settings.go +++ b/routes/settings.go @@ -2,6 +2,7 @@ package routes import ( "fmt" + "strconv" "github.com/gorilla/schema" conf "github.com/muety/wakapi/config" "github.com/muety/wakapi/models" @@ -14,13 +15,15 @@ import ( type SettingsHandler struct { config *conf.Config userSrvc *services.UserService + customRuleSrvc *services.CustomRuleService } var credentialsDecoder = schema.NewDecoder() -func NewSettingsHandler(userService *services.UserService) *SettingsHandler { +func NewSettingsHandler(userService *services.UserService, customRuleService *services.CustomRuleService) *SettingsHandler { return &SettingsHandler{ config: conf.Get(), + customRuleSrvc: customRuleService, userSrvc: userService, } } @@ -31,14 +34,14 @@ func (h *SettingsHandler) GetIndex(w http.ResponseWriter, r *http.Request) { } user := r.Context().Value(models.UserKey).(*models.User) + rules, _ := h.customRuleSrvc.GetCustomRuleForUser(user.ID) data := map[string]interface{}{ "User": user, + "Rules": rules, + "Success": r.FormValue("success"), + "Error": r.FormValue("error"), } - // TODO: when alerts are present, other data will not be passed to the template - if handleAlerts(w, r, conf.SettingsTemplate) { - return - } templates[conf.SettingsTemplate].Execute(w, data) } @@ -105,6 +108,56 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound) } +func (h *SettingsHandler) DeleteCustomRule(w http.ResponseWriter, r *http.Request) { + if h.config.IsDev() { + loadTemplates() + } + + user := r.Context().Value(models.UserKey).(*models.User) + ruleId, err := strconv.Atoi(r.PostFormValue("ruleid")) + if err != nil { + respondAlert(w, "internal server error", "", "settings.tpl.html", http.StatusInternalServerError) + return + } + + rule := &models.CustomRule{ + ID: uint(ruleId), + UserID: user.ID, + }; + h.customRuleSrvc.Delete(rule) + msg := url.QueryEscape("Custom rule deleted successfully."); + + http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound) +} + +func (h *SettingsHandler) PostCreateCustomRule(w http.ResponseWriter, r *http.Request) { + if h.config.IsDev() { + loadTemplates() + } + user := r.Context().Value(models.UserKey).(*models.User) + extension := r.PostFormValue("extension") + language := r.PostFormValue("language") + + if extension[0] == '.' { + extension = extension[1:] + } + + rule := &models.CustomRule{ + UserID: user.ID, + Extension: extension, + Language: language, + }; + + if _, err := h.customRuleSrvc.Create(rule); err != nil { + respondAlert(w, "internal server error", "", "settings.tpl.html", http.StatusInternalServerError) + return + } + + msg := url.QueryEscape("Custom rule saved successfully."); + + http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound) +} + func (h *SettingsHandler) PostResetApiKey(w http.ResponseWriter, r *http.Request) { if h.config.IsDev() { loadTemplates() diff --git a/services/custom_rule.go b/services/custom_rule.go new file mode 100644 index 0000000..6926b51 --- /dev/null +++ b/services/custom_rule.go @@ -0,0 +1,53 @@ +package services + +import ( + "github.com/jinzhu/gorm" + "github.com/muety/wakapi/models" +) + +type CustomRuleService struct { + Config *models.Config + Db *gorm.DB +} + +func NewCustomRuleService(db *gorm.DB) *CustomRuleService { + return &CustomRuleService{ + Config: models.GetConfig(), + Db: db, + } +} + +func (srv *CustomRuleService) GetCustomRuleById(CustomRuleId uint) (*models.CustomRule, error) { + r := &models.CustomRule{} + if err := srv.Db.Where(&models.CustomRule{ID: CustomRuleId}).First(r).Error; err != nil { + return r, err + } + return r, nil +} + +func (srv *CustomRuleService) GetCustomRuleForUser(userId string) ([]*models.CustomRule, error) { + var rules []*models.CustomRule + if err := srv.Db. + Where(&models.CustomRule{UserID: userId}). + Find(&rules).Error; err != nil { + return rules, err + } + + return rules, nil +} + +func (srv *CustomRuleService) Create(rule *models.CustomRule) (*models.CustomRule, error) { + result := srv.Db.Create(rule) + if err := result.Error; err != nil { + return nil, err + } + + return rule, nil +} + +func (srv *CustomRuleService) Delete(rule *models.CustomRule) { + srv.Db. + Where("id = ?", rule.ID). + Where("user_id = ?", rule.UserID). + Delete(models.CustomRule{}) +} diff --git a/services/summary.go b/services/summary.go index a061812..2d6b99d 100644 --- a/services/summary.go +++ b/services/summary.go @@ -17,20 +17,22 @@ import ( const HeartbeatDiffThreshold = 2 * time.Minute type SummaryService struct { - Config *config.Config - Cache *cache.Cache - Db *gorm.DB - HeartbeatService *HeartbeatService - AliasService *AliasService + Config *config.Config + Cache *cache.Cache + Db *gorm.DB + HeartbeatService *HeartbeatService + AliasService *AliasService + CustomRuleService *CustomRuleService } -func NewSummaryService(db *gorm.DB, heartbeatService *HeartbeatService, aliasService *AliasService) *SummaryService { +func NewSummaryService(db *gorm.DB, heartbeatService *HeartbeatService, aliasService *AliasService, customRuleService *CustomRuleService) *SummaryService { return &SummaryService{ Config: config.Get(), Cache: cache.New(24*time.Hour, 24*time.Hour), Db: db, HeartbeatService: heartbeatService, AliasService: aliasService, + CustomRuleService: customRuleService, } } diff --git a/static/assets/app.js b/static/assets/app.js index 1fd2931..5a980de 100644 --- a/static/assets/app.js +++ b/static/assets/app.js @@ -203,7 +203,7 @@ function togglePlaceholders(mask) { } function getPresentDataMask() { - return data.map(list => list.reduce((acc, e) => acc + e.total, 0) > 0) + return data.map(list => list ? list.reduce((acc, e) => acc + e.total, 0) : 0 > 0) } function getContainer(chart) { @@ -303,4 +303,4 @@ window.addEventListener('load', function () { setTopLabels() togglePlaceholders(getPresentDataMask()) draw() -}) \ No newline at end of file +}) diff --git a/views/settings.tpl.html b/views/settings.tpl.html index 869c793..511a9da 100644 --- a/views/settings.tpl.html +++ b/views/settings.tpl.html @@ -32,8 +32,7 @@ - + name="password_new" placeholder="Choose a password" minlength="6" required>
+
+
+ Custom Rules +
+ + {{ if .Rules }} + + {{ range $i, $rule := .Rules }} +
+ + {{ $rule.Extension }} + + {{ $rule.Language }} + +
+ + +
+
+ {{end}} + {{else}} +
+ No rules. +
+ {{end}} + + +
+
+ + + +
+
+ + +
+
+ +
+
+ +
+
Badges @@ -103,7 +154,8 @@
-

You can also add /project:your-cool-project to the URL to filter by project.

+ < + zzp>You can also add /project:your-cool-project to the URL to filter by project.

{{ else }}

You have the ability to create badges from your coding statistics using Shields.io. To do so, you need to grant public, unauthorized access to the respective endpoint.

@@ -136,4 +188,4 @@ {{ template "foot.tpl.html" . }} - \ No newline at end of file + diff --git a/wait-for-it.sh b/wait-for-it.sh index 50b69f5..d51f92c 100755 --- a/wait-for-it.sh +++ b/wait-for-it.sh @@ -1,9 +1,9 @@ #!/bin/bash if [ "$WAKAPI_DB_TYPE" != "sqlite3" ]; then - echo "Waiting 10 Seconds for DB to start" - sleep 10; + echo "Waiting 3 Seconds for DB to start" + sleep 3; fi echo "Starting Application" -./wakapi \ No newline at end of file +./wakapi From 395d039d418d2261ff1595415eb9e5d4316b631a Mon Sep 17 00:00:00 2001 From: Roch D'Amour Date: Sun, 25 Oct 2020 22:00:24 -0400 Subject: [PATCH 3/6] General cleanup and fixed PR comments --- routes/settings.go | 4 ++-- services/custom_rule.go | 9 +++++---- static/assets/app.js | 1 + views/settings.tpl.html | 7 ++++--- wait-for-it.sh | 6 +++--- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/routes/settings.go b/routes/settings.go index cc61d4d..51aba6d 100644 --- a/routes/settings.go +++ b/routes/settings.go @@ -116,7 +116,7 @@ func (h *SettingsHandler) DeleteCustomRule(w http.ResponseWriter, r *http.Reques user := r.Context().Value(models.UserKey).(*models.User) ruleId, err := strconv.Atoi(r.PostFormValue("ruleid")) if err != nil { - respondAlert(w, "internal server error", "", "settings.tpl.html", http.StatusInternalServerError) + respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) return } @@ -149,7 +149,7 @@ func (h *SettingsHandler) PostCreateCustomRule(w http.ResponseWriter, r *http.Re }; if _, err := h.customRuleSrvc.Create(rule); err != nil { - respondAlert(w, "internal server error", "", "settings.tpl.html", http.StatusInternalServerError) + respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) return } diff --git a/services/custom_rule.go b/services/custom_rule.go index 6926b51..be0784d 100644 --- a/services/custom_rule.go +++ b/services/custom_rule.go @@ -2,24 +2,25 @@ package services import ( "github.com/jinzhu/gorm" + "github.com/muety/wakapi/config" "github.com/muety/wakapi/models" ) type CustomRuleService struct { - Config *models.Config + Config *config.Config Db *gorm.DB } func NewCustomRuleService(db *gorm.DB) *CustomRuleService { return &CustomRuleService{ - Config: models.GetConfig(), + Config: config.Get(), Db: db, } } -func (srv *CustomRuleService) GetCustomRuleById(CustomRuleId uint) (*models.CustomRule, error) { +func (srv *CustomRuleService) GetCustomRuleById(customRuleId uint) (*models.CustomRule, error) { r := &models.CustomRule{} - if err := srv.Db.Where(&models.CustomRule{ID: CustomRuleId}).First(r).Error; err != nil { + if err := srv.Db.Where(&models.CustomRule{ID: customRuleId}).First(r).Error; err != nil { return r, err } return r, nil diff --git a/static/assets/app.js b/static/assets/app.js index 5a980de..4fe94a3 100644 --- a/static/assets/app.js +++ b/static/assets/app.js @@ -304,3 +304,4 @@ window.addEventListener('load', function () { togglePlaceholders(getPresentDataMask()) draw() }) + diff --git a/views/settings.tpl.html b/views/settings.tpl.html index 511a9da..a814788 100644 --- a/views/settings.tpl.html +++ b/views/settings.tpl.html @@ -32,7 +32,8 @@
+ name="password_new" placeholder="Choose a password" minlength="6" required> +
- < - zzp>You can also add /project:your-cool-project to the URL to filter by project.

+

You can also add /project:your-cool-project to the URL to filter by project.

{{ else }}

You have the ability to create badges from your coding statistics using Shields.io. To do so, you need to grant public, unauthorized access to the respective endpoint.

@@ -189,3 +189,4 @@ + diff --git a/wait-for-it.sh b/wait-for-it.sh index d51f92c..50b69f5 100755 --- a/wait-for-it.sh +++ b/wait-for-it.sh @@ -1,9 +1,9 @@ #!/bin/bash if [ "$WAKAPI_DB_TYPE" != "sqlite3" ]; then - echo "Waiting 3 Seconds for DB to start" - sleep 3; + echo "Waiting 10 Seconds for DB to start" + sleep 10; fi echo "Starting Application" -./wakapi +./wakapi \ No newline at end of file From 178c417757024b028c8b3a32f20ab6b82072eb09 Mon Sep 17 00:00:00 2001 From: Roch D'Amour Date: Sun, 25 Oct 2020 22:19:56 -0400 Subject: [PATCH 4/6] Keep the old configuration rules behaviour --- models/heartbeat.go | 19 ++++++++++++++++++- routes/heartbeat.go | 3 ++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/models/heartbeat.go b/models/heartbeat.go index bc17624..1da10e6 100644 --- a/models/heartbeat.go +++ b/models/heartbeat.go @@ -28,7 +28,24 @@ func (h *Heartbeat) Valid() bool { return h.User != nil && h.UserID != "" && h.Time != CustomTime(time.Time{}) } -func (h *Heartbeat) Augment(customRules []*CustomRule) { +func (h *Heartbeat) AugmentWithConfigRules(customLangs map[string]string) { + if h.Language == "" { + if h.languageRegex == nil { + h.languageRegex = regexp.MustCompile(`^.+\.(.+)$`) + } + groups := h.languageRegex.FindAllStringSubmatch(h.Entity, -1) + if len(groups) == 0 || len(groups[0]) != 2 { + return + } + ending := groups[0][1] + if _, ok := customLangs[ending]; !ok { + return + } + h.Language, _ = customLangs[ending] + } +} + +func (h *Heartbeat) AugmentWithUserRules(customRules []*CustomRule) { for _, lang := range customRules { reg := fmt.Sprintf(".*%s$", lang.Extension) match, err := regexp.MatchString(reg, h.Entity) diff --git a/routes/heartbeat.go b/routes/heartbeat.go index 5c52e8e..825e8d5 100644 --- a/routes/heartbeat.go +++ b/routes/heartbeat.go @@ -56,7 +56,8 @@ func (h *HeartbeatHandler) ApiPost(w http.ResponseWriter, r *http.Request) { hb.Machine = machineName hb.User = user hb.UserID = user.ID - hb.Augment(rules) + hb.AugmentWithConfigRules(h.config.App.CustomLanguages) + hb.AugmentWithUserRules(rules) if !hb.Valid() { w.WriteHeader(http.StatusBadRequest) From 86fc751e5860be5a6d54d98fe68adebdd0fb9c60 Mon Sep 17 00:00:00 2001 From: Roch D'Amour Date: Sun, 25 Oct 2020 23:19:16 -0400 Subject: [PATCH 5/6] Cache rules per user, invalidate cache on update --- docker-compose.yml | 2 +- services/custom_rule.go | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 36ce414..275c6df 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: WAKAPI_DB_PASSWORD: "asdfasdfasdf" WAKAPI_DB_HOST: "db" WAKAPI_DB_PORT: "5432" - ENV: "dev" + ENVIRONMENT: "dev" db: image: postgres:12.3 diff --git a/services/custom_rule.go b/services/custom_rule.go index be0784d..58c0079 100644 --- a/services/custom_rule.go +++ b/services/custom_rule.go @@ -4,17 +4,21 @@ import ( "github.com/jinzhu/gorm" "github.com/muety/wakapi/config" "github.com/muety/wakapi/models" + "github.com/patrickmn/go-cache" + "time" ) type CustomRuleService struct { Config *config.Config Db *gorm.DB + cache *cache.Cache } func NewCustomRuleService(db *gorm.DB) *CustomRuleService { return &CustomRuleService{ Config: config.Get(), Db: db, + cache: cache.New(1*time.Hour, 2*time.Hour), } } @@ -28,12 +32,16 @@ func (srv *CustomRuleService) GetCustomRuleById(customRuleId uint) (*models.Cust func (srv *CustomRuleService) GetCustomRuleForUser(userId string) ([]*models.CustomRule, error) { var rules []*models.CustomRule + if rules, found := srv.cache.Get(userId); found { + return rules.([]*models.CustomRule), nil; + } + if err := srv.Db. Where(&models.CustomRule{UserID: userId}). Find(&rules).Error; err != nil { return rules, err } - + srv.cache.Set(userId, rules, cache.DefaultExpiration) return rules, nil } @@ -42,6 +50,7 @@ func (srv *CustomRuleService) Create(rule *models.CustomRule) (*models.CustomRul if err := result.Error; err != nil { return nil, err } + srv.cache.Delete(rule.UserID) return rule, nil } @@ -51,4 +60,5 @@ func (srv *CustomRuleService) Delete(rule *models.CustomRule) { Where("id = ?", rule.ID). Where("user_id = ?", rule.UserID). Delete(models.CustomRule{}) + srv.cache.Delete(rule.UserID) } From 3f973a28ea129bf59700bfd4fc4f27b6c5f85a14 Mon Sep 17 00:00:00 2001 From: Roch D'Amour Date: Mon, 26 Oct 2020 00:27:07 -0400 Subject: [PATCH 6/6] Fixed custom rule settings CSS --- models/customrule.go | 2 +- views/settings.tpl.html | 51 +++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/models/customrule.go b/models/customrule.go index c97a633..563ae57 100644 --- a/models/customrule.go +++ b/models/customrule.go @@ -14,5 +14,5 @@ func validateLanguage(language string) bool { } func validateExtension(extension string) bool { - return len(extension) >= 2 + return len(extension) >= 1 } diff --git a/views/settings.tpl.html b/views/settings.tpl.html index a814788..be9b1ba 100644 --- a/views/settings.tpl.html +++ b/views/settings.tpl.html @@ -70,42 +70,43 @@
Custom Rules
+ +
+ Custom rules modify future coding activity, they’re useful for correcting languages displayed wrong on your dashboard. +
{{ if .Rules }} - {{ range $i, $rule := .Rules }} -
- - {{ $rule.Extension }} - - {{ $rule.Language }} +
+ + {{ $rule.Extension }} + + {{ $rule.Language }} -
- - -
-
- {{end}} - {{else}} -
- No rules. -
+
+ + +
+
+ {{end}} + {{else}} +
+ No rules. +
{{end}} - - -
-
+ +
-
-
+
-