2020-06-07 20:28:32 +03:00
package routes
import (
2021-01-30 12:54:54 +03:00
"encoding/base64"
2021-02-07 00:32:03 +03:00
"errors"
2020-06-07 20:28:32 +03:00
"fmt"
2021-01-30 13:17:37 +03:00
"github.com/emvi/logbuch"
2021-01-30 12:34:52 +03:00
"github.com/gorilla/mux"
2020-06-07 20:28:32 +03:00
"github.com/gorilla/schema"
2020-10-09 22:37:16 +03:00
conf "github.com/muety/wakapi/config"
2021-02-03 23:28:02 +03:00
"github.com/muety/wakapi/middlewares"
2020-06-07 20:28:32 +03:00
"github.com/muety/wakapi/models"
2020-11-06 23:19:54 +03:00
"github.com/muety/wakapi/models/view"
2020-06-07 20:28:32 +03:00
"github.com/muety/wakapi/services"
2021-02-05 20:47:28 +03:00
"github.com/muety/wakapi/services/imports"
2020-06-07 20:28:32 +03:00
"github.com/muety/wakapi/utils"
"net/http"
2020-10-27 00:34:50 +03:00
"strconv"
2021-01-30 12:54:54 +03:00
"time"
2020-06-07 20:28:32 +03:00
)
type SettingsHandler struct {
2020-11-01 22:14:10 +03:00
config * conf . Config
2020-11-08 12:12:49 +03:00
userSrvc services . IUserService
summarySrvc services . ISummaryService
2021-02-05 20:47:28 +03:00
heartbeatSrvc services . IHeartbeatService
2021-01-21 02:26:52 +03:00
aliasSrvc services . IAliasService
2020-11-08 12:12:49 +03:00
aggregationSrvc services . IAggregationService
languageMappingSrvc services . ILanguageMappingService
2021-02-05 20:47:28 +03:00
keyValueSrvc services . IKeyValueService
2021-01-30 12:54:54 +03:00
httpClient * http . Client
2020-06-07 20:28:32 +03:00
}
var credentialsDecoder = schema . NewDecoder ( )
2021-02-05 20:47:28 +03:00
func NewSettingsHandler (
userService services . IUserService ,
heartbeatService services . IHeartbeatService ,
summaryService services . ISummaryService ,
aliasService services . IAliasService ,
aggregationService services . IAggregationService ,
languageMappingService services . ILanguageMappingService ,
keyValueService services . IKeyValueService ,
) * SettingsHandler {
2020-06-07 20:28:32 +03:00
return & SettingsHandler {
2020-11-01 22:14:10 +03:00
config : conf . Get ( ) ,
2020-11-06 19:09:41 +03:00
summarySrvc : summaryService ,
2021-01-21 02:26:52 +03:00
aliasSrvc : aliasService ,
2020-11-06 19:09:41 +03:00
aggregationSrvc : aggregationService ,
2020-11-01 22:14:10 +03:00
languageMappingSrvc : languageMappingService ,
userSrvc : userService ,
2021-02-05 20:47:28 +03:00
heartbeatSrvc : heartbeatService ,
keyValueSrvc : keyValueService ,
2021-01-30 12:54:54 +03:00
httpClient : & http . Client { Timeout : 10 * time . Second } ,
2020-06-07 20:28:32 +03:00
}
}
2021-01-30 12:34:52 +03:00
func ( h * SettingsHandler ) RegisterRoutes ( router * mux . Router ) {
2021-02-03 23:28:02 +03:00
r := router . PathPrefix ( "/settings" ) . Subrouter ( )
r . Use (
2021-02-06 22:09:08 +03:00
middlewares . NewAuthenticateMiddleware ( h . userSrvc ) . Handler ,
2021-02-03 23:28:02 +03:00
)
r . Methods ( http . MethodGet ) . HandlerFunc ( h . GetIndex )
r . Methods ( http . MethodPost ) . HandlerFunc ( h . PostIndex )
2021-01-30 12:34:52 +03:00
}
2020-06-07 20:28:32 +03:00
func ( h * SettingsHandler ) GetIndex ( w http . ResponseWriter , r * http . Request ) {
if h . config . IsDev ( ) {
loadTemplates ( )
}
2020-11-06 23:19:54 +03:00
templates [ conf . SettingsTemplate ] . Execute ( w , h . buildViewModel ( r ) )
2020-06-07 20:28:32 +03:00
}
2021-02-03 22:53:27 +03:00
func ( h * SettingsHandler ) PostIndex ( w http . ResponseWriter , r * http . Request ) {
2020-06-07 20:28:32 +03:00
if h . config . IsDev ( ) {
loadTemplates ( )
}
if err := r . ParseForm ( ) ; err != nil {
2020-11-06 23:19:54 +03:00
w . WriteHeader ( http . StatusBadRequest )
2021-02-03 22:53:27 +03:00
templates [ conf . SettingsTemplate ] . Execute ( w , h . buildViewModel ( r ) . WithError ( "missing form values" ) )
2020-06-07 20:28:32 +03:00
return
}
2021-02-03 22:53:27 +03:00
action := r . PostForm . Get ( "action" )
r . PostForm . Del ( "action" )
actionFunc := h . dispatchAction ( action )
if actionFunc == nil {
logbuch . Warn ( "failed to dispatch action '%s'" , action )
2020-11-06 23:19:54 +03:00
w . WriteHeader ( http . StatusBadRequest )
2021-02-03 22:53:27 +03:00
templates [ conf . SettingsTemplate ] . Execute ( w , h . buildViewModel ( r ) . WithError ( "unknown action requests" ) )
2020-06-07 20:28:32 +03:00
return
}
2021-02-03 22:53:27 +03:00
status , successMsg , errorMsg := actionFunc ( w , r )
// action responded itself
if status == - 1 {
2020-06-07 20:28:32 +03:00
return
}
2021-02-03 22:53:27 +03:00
if errorMsg != "" {
2021-02-05 20:47:28 +03:00
w . WriteHeader ( status )
2021-02-03 22:53:27 +03:00
templates [ conf . SettingsTemplate ] . Execute ( w , h . buildViewModel ( r ) . WithError ( errorMsg ) )
2020-06-07 20:28:32 +03:00
return
}
2021-02-03 22:53:27 +03:00
if successMsg != "" {
2021-02-05 20:47:28 +03:00
w . WriteHeader ( status )
2021-02-03 22:53:27 +03:00
templates [ conf . SettingsTemplate ] . Execute ( w , h . buildViewModel ( r ) . WithSuccess ( successMsg ) )
return
}
templates [ conf . SettingsTemplate ] . Execute ( w , h . buildViewModel ( r ) )
}
func ( h * SettingsHandler ) dispatchAction ( action string ) action {
switch action {
case "change_password" :
return h . actionChangePassword
case "reset_apikey" :
return h . actionResetApiKey
case "delete_alias" :
return h . actionDeleteAlias
case "add_alias" :
return h . actionAddAlias
case "delete_mapping" :
return h . actionDeleteLanguageMapping
case "add_mapping" :
return h . actionAddLanguageMapping
2021-02-07 00:32:03 +03:00
case "update_sharing" :
return h . actionUpdateSharing
2021-02-03 22:53:27 +03:00
case "toggle_wakatime" :
return h . actionSetWakatimeApiKey
2021-02-05 20:47:28 +03:00
case "import_wakatime" :
return h . actionImportWaktime
2021-02-03 22:53:27 +03:00
case "regenerate_summaries" :
return h . actionRegenerateSummaries
case "delete_account" :
return h . actionDeleteUser
}
return nil
}
func ( h * SettingsHandler ) actionChangePassword ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
if h . config . IsDev ( ) {
loadTemplates ( )
}
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
var credentials models . CredentialsReset
if err := r . ParseForm ( ) ; err != nil {
return http . StatusBadRequest , "" , "missing parameters"
}
if err := credentialsDecoder . Decode ( & credentials , r . PostForm ) ; err != nil {
return http . StatusBadRequest , "" , "missing parameters"
}
if ! utils . CompareBcrypt ( user . Password , credentials . PasswordOld , h . config . Security . PasswordSalt ) {
return http . StatusUnauthorized , "" , "invalid credentials"
}
if ! credentials . IsValid ( ) {
return http . StatusBadRequest , "" , "invalid parameters"
}
2020-06-07 20:28:32 +03:00
user . Password = credentials . PasswordNew
2020-10-16 17:11:14 +03:00
if hash , err := utils . HashBcrypt ( user . Password , h . config . Security . PasswordSalt ) ; err != nil {
2021-02-03 22:53:27 +03:00
return http . StatusInternalServerError , "" , "internal server error"
2020-10-16 17:11:14 +03:00
} else {
user . Password = hash
2020-06-07 20:28:32 +03:00
}
if _ , err := h . userSrvc . Update ( user ) ; err != nil {
2021-02-03 22:53:27 +03:00
return http . StatusInternalServerError , "" , "internal server error"
2020-06-07 20:28:32 +03:00
}
login := & models . Login {
Username : user . ID ,
Password : user . Password ,
}
2021-01-31 16:34:54 +03:00
encoded , err := h . config . Security . SecureCookie . Encode ( models . AuthCookieKey , login . Username )
2020-06-07 20:28:32 +03:00
if err != nil {
2021-02-03 22:53:27 +03:00
return http . StatusInternalServerError , "" , "internal server error"
2020-06-07 20:28:32 +03:00
}
2020-11-22 00:30:56 +03:00
http . SetCookie ( w , h . config . CreateCookie ( models . AuthCookieKey , encoded , "/" ) )
2021-02-03 22:53:27 +03:00
return http . StatusOK , "password was updated successfully" , ""
2020-10-25 09:22:10 +03:00
}
2021-02-03 22:53:27 +03:00
func ( h * SettingsHandler ) actionResetApiKey ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
2020-10-25 09:22:10 +03:00
if h . config . IsDev ( ) {
loadTemplates ( )
}
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
2021-02-03 22:53:27 +03:00
if _ , err := h . userSrvc . ResetApiKey ( user ) ; err != nil {
return http . StatusInternalServerError , "" , "internal server error"
2020-10-25 09:22:10 +03:00
}
2021-02-03 22:53:27 +03:00
msg := fmt . Sprintf ( "your new api key is: %s" , user . ApiKey )
return http . StatusOK , msg , ""
2020-06-07 20:28:32 +03:00
}
2021-02-07 00:32:03 +03:00
func ( h * SettingsHandler ) actionUpdateSharing ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
if h . config . IsDev ( ) {
loadTemplates ( )
}
var err error
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
defer h . userSrvc . FlushCache ( )
user . ShareProjects , err = strconv . ParseBool ( r . PostFormValue ( "share_projects" ) )
user . ShareLanguages , err = strconv . ParseBool ( r . PostFormValue ( "share_languages" ) )
user . ShareEditors , err = strconv . ParseBool ( r . PostFormValue ( "share_editors" ) )
user . ShareOSs , err = strconv . ParseBool ( r . PostFormValue ( "share_oss" ) )
user . ShareMachines , err = strconv . ParseBool ( r . PostFormValue ( "share_machines" ) )
if v , e := strconv . Atoi ( r . PostFormValue ( "max_days" ) ) ; e == nil && v >= 0 {
user . ShareDataMaxDays = uint ( v )
} else {
err = errors . New ( "" )
}
if err != nil {
return http . StatusBadRequest , "" , "invalid input"
}
if _ , err := h . userSrvc . Update ( user ) ; err != nil {
return http . StatusInternalServerError , "" , "internal sever error"
}
return http . StatusOK , "settings updated" , ""
}
2021-02-03 22:53:27 +03:00
func ( h * SettingsHandler ) actionDeleteAlias ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
2021-01-21 02:26:52 +03:00
if h . config . IsDev ( ) {
loadTemplates ( )
}
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
aliasKey := r . PostFormValue ( "key" )
aliasType , err := strconv . Atoi ( r . PostFormValue ( "type" ) )
if err != nil {
aliasType = 99 // nothing will be found later on
}
if aliases , err := h . aliasSrvc . GetByUserAndKeyAndType ( user . ID , aliasKey , uint8 ( aliasType ) ) ; err != nil {
2021-02-03 22:53:27 +03:00
return http . StatusNotFound , "" , "aliases not found"
2021-01-21 02:26:52 +03:00
} else if err := h . aliasSrvc . DeleteMulti ( aliases ) ; err != nil {
2021-02-03 22:53:27 +03:00
return http . StatusInternalServerError , "" , "could not delete aliases"
2021-01-21 02:26:52 +03:00
}
2021-02-03 22:53:27 +03:00
return http . StatusOK , "aliases deleted successfully" , ""
2021-01-21 02:26:52 +03:00
}
2021-02-03 22:53:27 +03:00
func ( h * SettingsHandler ) actionAddAlias ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
2021-01-21 02:26:52 +03:00
if h . config . IsDev ( ) {
loadTemplates ( )
}
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
aliasKey := r . PostFormValue ( "key" )
aliasValue := r . PostFormValue ( "value" )
aliasType , err := strconv . Atoi ( r . PostFormValue ( "type" ) )
if err != nil {
aliasType = 99 // Alias.IsValid() will return false later on
}
alias := & models . Alias {
UserID : user . ID ,
Key : aliasKey ,
Value : aliasValue ,
Type : uint8 ( aliasType ) ,
}
if _ , err := h . aliasSrvc . Create ( alias ) ; err != nil {
// TODO: distinguish between bad request, conflict and server error
2021-02-03 22:53:27 +03:00
return http . StatusBadRequest , "" , "invalid input"
2021-01-21 02:26:52 +03:00
}
2021-02-03 22:53:27 +03:00
return http . StatusOK , "alias added successfully" , ""
2021-01-21 02:26:52 +03:00
}
2021-02-03 22:53:27 +03:00
func ( h * SettingsHandler ) actionDeleteLanguageMapping ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
2020-06-07 20:28:32 +03:00
if h . config . IsDev ( ) {
loadTemplates ( )
}
2020-06-07 20:58:06 +03:00
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
2021-02-03 22:53:27 +03:00
id , err := strconv . Atoi ( r . PostFormValue ( "mapping_id" ) )
if err != nil {
return http . StatusInternalServerError , "" , "could not delete mapping"
2020-06-07 20:58:06 +03:00
}
2021-02-03 22:53:27 +03:00
if mapping , err := h . languageMappingSrvc . GetById ( uint ( id ) ) ; err != nil || mapping == nil {
return http . StatusNotFound , "" , "mapping not found"
} else if mapping . UserID != user . ID {
return http . StatusForbidden , "" , "not allowed to delete mapping"
}
if err := h . languageMappingSrvc . Delete ( & models . LanguageMapping { ID : uint ( id ) } ) ; err != nil {
return http . StatusInternalServerError , "" , "could not delete mapping"
}
return http . StatusOK , "mapping deleted successfully" , ""
2020-06-07 20:28:32 +03:00
}
2020-09-12 17:09:23 +03:00
2021-02-03 22:53:27 +03:00
func ( h * SettingsHandler ) actionAddLanguageMapping ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
2021-01-22 01:26:50 +03:00
if h . config . IsDev ( ) {
loadTemplates ( )
}
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
2021-02-03 22:53:27 +03:00
extension := r . PostFormValue ( "extension" )
language := r . PostFormValue ( "language" )
2021-01-30 12:54:54 +03:00
2021-02-03 22:53:27 +03:00
if extension [ 0 ] == '.' {
extension = extension [ 1 : ]
2021-01-30 12:54:54 +03:00
}
2021-02-03 22:53:27 +03:00
mapping := & models . LanguageMapping {
UserID : user . ID ,
Extension : extension ,
Language : language ,
2021-01-22 01:26:50 +03:00
}
2021-02-03 22:53:27 +03:00
if _ , err := h . languageMappingSrvc . Create ( mapping ) ; err != nil {
return http . StatusConflict , "" , "mapping already exists"
}
return http . StatusOK , "mapping added successfully" , ""
2021-01-22 01:26:50 +03:00
}
2021-02-03 22:53:27 +03:00
func ( h * SettingsHandler ) actionSetWakatimeApiKey ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
2021-02-03 00:54:22 +03:00
if h . config . IsDev ( ) {
loadTemplates ( )
}
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
2021-02-03 22:53:27 +03:00
apiKey := r . PostFormValue ( "api_key" )
2021-02-03 00:54:22 +03:00
2021-02-03 22:53:27 +03:00
// Healthcheck, if a new API key is set, i.e. the feature is activated
if ( user . WakatimeApiKey == "" && apiKey != "" ) && ! h . validateWakatimeKey ( apiKey ) {
return http . StatusBadRequest , "" , "failed to connect to WakaTime, API key invalid?"
}
if _ , err := h . userSrvc . SetWakatimeApiKey ( user , apiKey ) ; err != nil {
return http . StatusInternalServerError , "" , "internal server error"
}
return http . StatusOK , "Wakatime API Key updated successfully" , ""
2021-02-03 00:54:22 +03:00
}
2021-02-05 20:47:28 +03:00
func ( h * SettingsHandler ) actionImportWaktime ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
2020-11-06 19:09:41 +03:00
if h . config . IsDev ( ) {
loadTemplates ( )
}
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
2021-02-05 20:47:28 +03:00
if user . WakatimeApiKey == "" {
return http . StatusForbidden , "" , "not connected to wakatime"
}
2020-11-06 19:09:41 +03:00
2021-02-05 20:47:28 +03:00
kvKey := fmt . Sprintf ( "%s_%s" , conf . KeyLastImportImport , user . ID )
if ! h . config . IsDev ( ) {
lastImportKv := h . keyValueSrvc . MustGetString ( kvKey )
lastImport , _ := time . Parse ( time . RFC822 , lastImportKv . Value )
if time . Now ( ) . Sub ( lastImport ) < time . Duration ( h . config . App . ImportBackoffMin ) * time . Minute {
return http . StatusTooManyRequests ,
"" ,
fmt . Sprintf ( "Too many data imports. You are only allowed to request an import every %d minutes." , h . config . App . ImportBackoffMin )
}
2020-11-06 19:09:41 +03:00
}
2021-02-05 20:47:28 +03:00
go func ( user * models . User ) {
importer := imports . NewWakatimeHeartbeatImporter ( user . WakatimeApiKey )
countBefore , err := h . heartbeatSrvc . CountByUser ( user )
if err != nil {
println ( err )
}
2021-02-06 02:31:30 +03:00
var stream <- chan * models . Heartbeat
if latest , err := h . heartbeatSrvc . GetLatestByOriginAndUser ( imports . OriginWakatime , user ) ; latest == nil || err != nil {
stream = importer . ImportAll ( user )
} else {
// if an import has happened before, only import heartbeats newer than the latest of the last import
stream = importer . Import ( user , latest . Time . T ( ) , time . Now ( ) )
}
2021-02-05 20:47:28 +03:00
count := 0
batch := make ( [ ] * models . Heartbeat , 0 )
2021-02-06 02:31:30 +03:00
for hb := range stream {
2021-02-05 20:47:28 +03:00
count ++
batch = append ( batch , hb )
if len ( batch ) == h . config . App . ImportBatchSize {
if err := h . heartbeatSrvc . InsertBatch ( batch ) ; err != nil {
logbuch . Warn ( "failed to insert imported heartbeat, already existing? – %v" , err )
}
batch = make ( [ ] * models . Heartbeat , 0 )
}
}
countAfter , _ := h . heartbeatSrvc . CountByUser ( user )
logbuch . Info ( "downloaded %d heartbeats for user '%s' (%d actually imported)" , count , user . ID , countAfter - countBefore )
h . regenerateSummaries ( user )
} ( user )
h . keyValueSrvc . PutString ( & models . KeyStringValue {
Key : kvKey ,
Value : time . Now ( ) . Format ( time . RFC822 ) ,
} )
2021-02-03 22:53:27 +03:00
2021-02-06 02:31:30 +03:00
return http . StatusAccepted , "ImportAll started. This may take a few minutes." , ""
2021-02-05 20:47:28 +03:00
}
func ( h * SettingsHandler ) actionRegenerateSummaries ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
if h . config . IsDev ( ) {
loadTemplates ( )
}
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
if err := h . regenerateSummaries ( user ) ; err != nil {
return http . StatusInternalServerError , "" , "failed to regenerate summaries"
2020-11-06 19:09:41 +03:00
}
2021-02-03 22:53:27 +03:00
return http . StatusOK , "summaries are being regenerated – this may take a few seconds" , ""
}
func ( h * SettingsHandler ) actionDeleteUser ( w http . ResponseWriter , r * http . Request ) ( int , string , string ) {
if h . config . IsDev ( ) {
loadTemplates ( )
}
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
go func ( user * models . User ) {
logbuch . Info ( "deleting user '%s' shortly" , user . ID )
time . Sleep ( 5 * time . Minute )
if err := h . userSrvc . Delete ( user ) ; err != nil {
logbuch . Error ( "failed to delete user '%s' – %v" , user . ID , err )
} else {
logbuch . Info ( "successfully deleted user '%s'" , user . ID )
}
} ( user )
http . SetCookie ( w , h . config . GetClearCookie ( models . AuthCookieKey , "/" ) )
http . Redirect ( w , r , fmt . Sprintf ( "%s/?success=%s" , h . config . Server . BasePath , "Your account will be deleted in a few minutes. Sorry to you go." ) , http . StatusFound )
return - 1 , "" , ""
2020-11-06 23:19:54 +03:00
}
2021-01-30 12:54:54 +03:00
func ( h * SettingsHandler ) validateWakatimeKey ( apiKey string ) bool {
headers := http . Header {
"Accept" : [ ] string { "application/json" } ,
"Authorization" : [ ] string {
fmt . Sprintf ( "Basic %s" , base64 . StdEncoding . EncodeToString ( [ ] byte ( apiKey ) ) ) ,
} ,
}
request , err := http . NewRequest (
http . MethodGet ,
2021-02-05 20:47:28 +03:00
conf . WakatimeApiUrl + conf . WakatimeApiUserUrl ,
2021-01-30 12:54:54 +03:00
nil ,
)
if err != nil {
return false
}
request . Header = headers
response , err := h . httpClient . Do ( request )
if err != nil || response . StatusCode < 200 || response . StatusCode >= 300 {
return false
}
return true
}
2021-02-05 20:47:28 +03:00
func ( h * SettingsHandler ) regenerateSummaries ( user * models . User ) error {
logbuch . Info ( "clearing summaries for user '%s'" , user . ID )
if err := h . summarySrvc . DeleteByUser ( user . ID ) ; err != nil {
logbuch . Error ( "failed to clear summaries: %v" , err )
return err
}
if err := h . aggregationSrvc . Run ( map [ string ] bool { user . ID : true } ) ; err != nil {
logbuch . Error ( "failed to regenerate summaries: %v" , err )
return err
}
return nil
}
2020-11-06 23:19:54 +03:00
func ( h * SettingsHandler ) buildViewModel ( r * http . Request ) * view . SettingsViewModel {
user := r . Context ( ) . Value ( models . UserKey ) . ( * models . User )
mappings , _ := h . languageMappingSrvc . GetByUser ( user . ID )
2021-01-21 02:26:52 +03:00
aliases , _ := h . aliasSrvc . GetByUser ( user . ID )
aliasMap := make ( map [ string ] [ ] * models . Alias )
for _ , a := range aliases {
k := fmt . Sprintf ( "%s_%d" , a . Key , a . Type )
if _ , ok := aliasMap [ k ] ; ! ok {
aliasMap [ k ] = [ ] * models . Alias { a }
} else {
aliasMap [ k ] = append ( aliasMap [ k ] , a )
}
}
combinedAliases := make ( [ ] * view . SettingsVMCombinedAlias , 0 )
for _ , l := range aliasMap {
ca := & view . SettingsVMCombinedAlias {
Key : l [ 0 ] . Key ,
Type : l [ 0 ] . Type ,
Values : make ( [ ] string , len ( l ) ) ,
}
for i , a := range l {
ca . Values [ i ] = a . Value
}
combinedAliases = append ( combinedAliases , ca )
}
2020-11-06 23:19:54 +03:00
return & view . SettingsViewModel {
User : user ,
LanguageMappings : mappings ,
2021-01-21 02:26:52 +03:00
Aliases : combinedAliases ,
2020-11-06 23:19:54 +03:00
Success : r . URL . Query ( ) . Get ( "success" ) ,
Error : r . URL . Query ( ) . Get ( "error" ) ,
}
2020-11-06 19:09:41 +03:00
}