From 769bffe6ff7481c8e2c9b2c9e8ba55a4714ebb22 Mon Sep 17 00:00:00 2001 From: Bagas Aulia Rezki Date: Fri, 26 Apr 2024 15:45:54 +0700 Subject: [PATCH] Added functionality to user page --- .idea/inspectionProfiles/Project_Default.xml | 10 +++ db/model/user/user.go | 20 +++-- handler/signin/signin.go | 77 +++++++++++++++++++- handler/user/user.go | 3 +- middleware/middleware.go | 2 + routes/routes.go | 6 ++ session/session.go | 55 ++++++++++---- types/types.go | 9 --- view/user/user.templ | 68 ++++++++--------- 9 files changed, 186 insertions(+), 64 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..eefade7 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/db/model/user/user.go b/db/model/user/user.go index 81917d3..bd2240c 100644 --- a/db/model/user/user.go +++ b/db/model/user/user.go @@ -4,23 +4,31 @@ import ( "fmt" "github.com/fossyy/filekeeper/db" "github.com/fossyy/filekeeper/logger" - "github.com/fossyy/filekeeper/types" + "github.com/google/uuid" "sync" "time" ) type Cache struct { - users map[string]*types.UserWithExpired + users map[string]*UserWithExpired mu sync.Mutex } +type UserWithExpired struct { + UserID uuid.UUID + Username string + Email string + Password string + AccessAt time.Time +} + var log *logger.AggregatedLogger var UserCache *Cache func init() { log = logger.Logger() - UserCache = &Cache{users: make(map[string]*types.UserWithExpired)} + UserCache = &Cache{users: make(map[string]*UserWithExpired)} ticker := time.NewTicker(time.Hour * 8) go func() { @@ -44,7 +52,7 @@ func init() { }() } -func Get(email string) (*types.UserWithExpired, error) { +func Get(email string) (*UserWithExpired, error) { UserCache.mu.Lock() defer UserCache.mu.Unlock() @@ -52,13 +60,13 @@ func Get(email string) (*types.UserWithExpired, error) { return user, nil } - var userData types.UserWithExpired + var userData UserWithExpired err := db.DB.Table("users").Where("email = ?", email).First(&userData).Error if err != nil { return nil, err } - UserCache.users[email] = &types.UserWithExpired{ + UserCache.users[email] = &UserWithExpired{ UserID: userData.UserID, Username: userData.Username, Email: userData.Email, diff --git a/handler/signin/signin.go b/handler/signin/signin.go index 13c8bd0..ef8a55a 100644 --- a/handler/signin/signin.go +++ b/handler/signin/signin.go @@ -9,6 +9,7 @@ import ( "github.com/fossyy/filekeeper/utils" signinView "github.com/fossyy/filekeeper/view/signin" "net/http" + "strings" ) var log *logger.AggregatedLogger @@ -64,8 +65,21 @@ func POST(w http.ResponseWriter, r *http.Request) { Authenticated: true, } + userAgent := r.Header.Get("User-Agent") + browserInfo, osInfo := parseUserAgent(userAgent) + + sessionInfo := session.SessionInfo{ + SessionID: storeSession.ID, + Browser: browserInfo["browser"], + Version: browserInfo["version"], + OS: osInfo["os"], + OSVersion: osInfo["version"], + IP: utils.ClientIP(r), + Location: "Indonesia", + } + storeSession.Save(w) - session.AppendSession(email, storeSession) + session.AppendSession(email, &sessionInfo) cookie, err := r.Cookie("redirect") if errors.Is(err, http.ErrNoCookie) { @@ -90,3 +104,64 @@ func POST(w http.ResponseWriter, r *http.Request) { return } } + +func parseUserAgent(userAgent string) (map[string]string, map[string]string) { + browserInfo := make(map[string]string) + osInfo := make(map[string]string) + if strings.Contains(userAgent, "Firefox") { + browserInfo["browser"] = "Firefox" + parts := strings.Split(userAgent, "Firefox/") + if len(parts) > 1 { + version := strings.Split(parts[1], " ")[0] + browserInfo["version"] = version + } + } else if strings.Contains(userAgent, "Chrome") { + browserInfo["browser"] = "Chrome" + parts := strings.Split(userAgent, "Chrome/") + if len(parts) > 1 { + version := strings.Split(parts[1], " ")[0] + browserInfo["version"] = version + } + } else { + browserInfo["browser"] = "Unknown" + browserInfo["version"] = "Unknown" + } + + if strings.Contains(userAgent, "Windows") { + osInfo["os"] = "Windows" + parts := strings.Split(userAgent, "Windows ") + if len(parts) > 1 { + version := strings.Split(parts[1], ";")[0] + osInfo["version"] = version + } + } else if strings.Contains(userAgent, "Macintosh") { + osInfo["os"] = "Mac OS" + parts := strings.Split(userAgent, "Mac OS X ") + if len(parts) > 1 { + version := strings.Split(parts[1], ";")[0] + osInfo["version"] = version + } + } else if strings.Contains(userAgent, "Linux") { + osInfo["os"] = "Linux" + osInfo["version"] = "Unknown" + } else if strings.Contains(userAgent, "Android") { + osInfo["os"] = "Android" + parts := strings.Split(userAgent, "Android ") + if len(parts) > 1 { + version := strings.Split(parts[1], ";")[0] + osInfo["version"] = version + } + } else if strings.Contains(userAgent, "iPhone") || strings.Contains(userAgent, "iPad") || strings.Contains(userAgent, "iPod") { + osInfo["os"] = "iOS" + parts := strings.Split(userAgent, "OS ") + if len(parts) > 1 { + version := strings.Split(parts[1], " ")[0] + osInfo["version"] = version + } + } else { + osInfo["os"] = "Unknown" + osInfo["version"] = "Unknown" + } + + return browserInfo, osInfo +} diff --git a/handler/user/user.go b/handler/user/user.go index 8b4cc59..a11d2e3 100644 --- a/handler/user/user.go +++ b/handler/user/user.go @@ -34,7 +34,8 @@ func GET(w http.ResponseWriter, r *http.Request) { log.Error(err.Error()) return } - component := userView.Main("User Page", userSession.Email, userSession.Username) + + component := userView.Main("User Page", userSession.Email, userSession.Username, session.UserSessions[userSession.Email]) err = component.Render(r.Context(), w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/middleware/middleware.go b/middleware/middleware.go index 55324a2..4b94b3e 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -75,6 +75,7 @@ func Auth(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) { return } storeSession, err := session.Store.Get(cookie.Value) + if err != nil { if errors.Is(err, &session.SessionNotFound{}) { storeSession.Destroy(w) @@ -87,6 +88,7 @@ func Auth(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) { } userSession := GetUser(storeSession) if userSession.Authenticated { + session.GetSessionInfo(storeSession.Values["user"].(types.User).Email, cookie.Value).UpdateAccessTime() next.ServeHTTP(w, r) return } diff --git a/routes/routes.go b/routes/routes.go index 4c5646c..58fdb63 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -1,6 +1,7 @@ package routes import ( + "encoding/json" downloadHandler "github.com/fossyy/filekeeper/handler/download" downloadFileHandler "github.com/fossyy/filekeeper/handler/download/file" forgotPasswordHandler "github.com/fossyy/filekeeper/handler/forgotPassword" @@ -15,6 +16,7 @@ import ( "github.com/fossyy/filekeeper/handler/upload/initialisation" userHandler "github.com/fossyy/filekeeper/handler/user" "github.com/fossyy/filekeeper/middleware" + "github.com/fossyy/filekeeper/session" "net/http" ) @@ -35,6 +37,10 @@ func SetupRoutes() *http.ServeMux { } }) + handler.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode(session.Getses()) + }) + handler.HandleFunc("/signin", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: diff --git a/session/session.go b/session/session.go index 67f1fac..0bef255 100644 --- a/session/session.go +++ b/session/session.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" "sync" + "time" ) type Session struct { @@ -17,8 +18,21 @@ type StoreSession struct { mu sync.Mutex } +type SessionInfo struct { + SessionID string + Browser string + Version string + OS string + OSVersion string + IP string + Location string + AccessAt string +} + +type ListSessionInfo map[string][]*SessionInfo + var Store = StoreSession{Sessions: make(map[string]*Session)} -var userSessions = make(map[string][]string) +var UserSessions = make(ListSessionInfo) type SessionNotFound struct{} @@ -68,33 +82,48 @@ func (s *Session) Destroy(w http.ResponseWriter) { }) } -func AppendSession(email string, session *Session) { - userSessions[email] = append(userSessions[email], session.ID) +func AppendSession(email string, sessionInfo *SessionInfo) { + UserSessions[email] = append(UserSessions[email], sessionInfo) } func RemoveSession(email string, id string) { - sessions := userSessions[email] - var updatedSessions []string + sessions := UserSessions[email] + var updatedSessions []*SessionInfo for _, userSession := range sessions { - if userSession != id { + if userSession.SessionID != id { updatedSessions = append(updatedSessions, userSession) } } if len(updatedSessions) > 0 { - userSessions[email] = updatedSessions + UserSessions[email] = updatedSessions return } - delete(userSessions, email) + delete(UserSessions, email) } func RemoveAllSession(email string) { - sessions := userSessions[email] + sessions := UserSessions[email] for _, session := range sessions { - delete(Store.Sessions, session) + delete(Store.Sessions, session.SessionID) } - delete(userSessions, email) + delete(UserSessions, email) } -func Getses() map[string][]string { - return userSessions +func GetSessionInfo(email string, id string) *SessionInfo { + for _, session := range UserSessions[email] { + if session.SessionID == id { + return session + } + } + return nil +} + +func (user *SessionInfo) UpdateAccessTime() { + currentTime := time.Now() + formattedTime := currentTime.Format("01-02-2006") + user.AccessAt = formattedTime +} + +func Getses() *ListSessionInfo { + return &UserSessions } diff --git a/types/types.go b/types/types.go index e7a2683..5744ef9 100644 --- a/types/types.go +++ b/types/types.go @@ -2,7 +2,6 @@ package types import ( "github.com/google/uuid" - "time" ) type Message struct { @@ -17,14 +16,6 @@ type User struct { Authenticated bool } -type UserWithExpired struct { - UserID uuid.UUID - Username string - Email string - Password string - AccessAt time.Time -} - type FileInfo struct { Name string `json:"name"` Size int `json:"size"` diff --git a/view/user/user.templ b/view/user/user.templ index 4e09a30..6ae8132 100644 --- a/view/user/user.templ +++ b/view/user/user.templ @@ -1,8 +1,11 @@ package userView -import "github.com/fossyy/filekeeper/view/layout" +import ( + "github.com/fossyy/filekeeper/view/layout" + "github.com/fossyy/filekeeper/session" +) -templ content(email string, username string, title string) { +templ content(email string, username string, title string, ListSession []*session.SessionInfo) { @layout.Base(title){
@@ -107,50 +110,33 @@ templ content(email string, username string, title string) { + for _, ses := range ListSession { - - 192.168.1.100 - Chrome 89 + {ses.IP} - Desktop + {ses.Browser + ses.Version} - 1 week - ago + {ses.OS + ses.OSVersion} + + {ses.AccessAt} - + + }
-
- -
-

- Logged in as John Doe -

-

- Logged in from 192.168.1.100 -

-

- Logged in at 2023-04-26 15:30:00 -

-
-
@@ -218,18 +204,32 @@ templ content(email string, username string, title string) {
Used - 12.5GB + 42.0GB
Available - 37.5GB + 6.9GB
+ +
@@ -262,6 +262,6 @@ templ content(email string, username string, title string) { } } -templ Main(title string, email string, username string) { - @content(email, username, title) +templ Main(title string, email string, username string, ListSession []*session.SessionInfo) { + @content(email, username, title, ListSession) } \ No newline at end of file