From 2a32c8b2f6d5ed37b5bcd6cca3ffbd73e5f50139 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 30 Jan 2024 17:57:23 +0800 Subject: [PATCH] :sparkles: Better Dashboard --- pkg/database/migrator.go | 1 + pkg/models/accounts.go | 1 + pkg/models/events.go | 12 ++ pkg/server/accounts_api.go | 28 +++++ pkg/server/challanges_api.go | 4 +- pkg/server/oauth_api.go | 9 +- pkg/server/startup.go | 2 + pkg/services/events.go | 20 ++++ view/src/layouts/RootLayout.tsx | 2 +- view/src/pages/auth/login.tsx | 2 +- view/src/pages/dashboard.tsx | 200 +++++++++++++++++++++++++++++++- view/src/stores/userinfo.tsx | 2 +- 12 files changed, 271 insertions(+), 12 deletions(-) create mode 100644 pkg/models/events.go create mode 100644 pkg/services/events.go diff --git a/pkg/database/migrator.go b/pkg/database/migrator.go index 2333f18..412b01e 100644 --- a/pkg/database/migrator.go +++ b/pkg/database/migrator.go @@ -15,6 +15,7 @@ func RunMigration(source *gorm.DB) error { &models.AuthChallenge{}, &models.MagicToken{}, &models.ThirdClient{}, + &models.ActionEvent{}, ); err != nil { return err } diff --git a/pkg/models/accounts.go b/pkg/models/accounts.go index e597edf..5fe7071 100644 --- a/pkg/models/accounts.go +++ b/pkg/models/accounts.go @@ -25,6 +25,7 @@ type Account struct { Challenges []AuthChallenge `json:"challenges"` Factors []AuthFactor `json:"factors"` Contacts []AccountContact `json:"contacts"` + Events []ActionEvent `json:"events"` MagicTokens []MagicToken `json:"-" gorm:"foreignKey:AssignTo"` ThirdClients []ThirdClient `json:"clients"` ConfirmedAt *time.Time `json:"confirmed_at"` diff --git a/pkg/models/events.go b/pkg/models/events.go new file mode 100644 index 0000000..5c2cea0 --- /dev/null +++ b/pkg/models/events.go @@ -0,0 +1,12 @@ +package models + +type ActionEvent struct { + BaseModel + + Type string `json:"type"` + Target string `json:"target"` + Location string `json:"location"` + IpAddress string `json:"ip_address"` + UserAgent string `json:"user_agent"` + AccountID uint `json:"account_id"` +} diff --git a/pkg/server/accounts_api.go b/pkg/server/accounts_api.go index 47bc349..014133d 100644 --- a/pkg/server/accounts_api.go +++ b/pkg/server/accounts_api.go @@ -27,6 +27,34 @@ func getPrincipal(c *fiber.Ctx) error { return c.JSON(data) } +func getEvents(c *fiber.Ctx) error { + user := c.Locals("principal").(models.Account) + take := c.QueryInt("take", 0) + offset := c.QueryInt("offset", 0) + + var count int64 + var events []models.ActionEvent + if err := database.C. + Where(&models.ActionEvent{AccountID: user.ID}). + Model(&models.ActionEvent{}). + Count(&count).Error; err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + if err := database.C. + Where(&models.ActionEvent{AccountID: user.ID}). + Limit(take). + Offset(offset). + Find(&events).Error; err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.JSON(fiber.Map{ + "count": count, + "data": events, + }) +} + func doRegister(c *fiber.Ctx) error { var data struct { Name string `json:"name"` diff --git a/pkg/server/challanges_api.go b/pkg/server/challanges_api.go index 4947850..3f3485f 100644 --- a/pkg/server/challanges_api.go +++ b/pkg/server/challanges_api.go @@ -32,6 +32,7 @@ func startChallenge(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusNotFound, err.Error()) } + services.AddEvent(user, "challenges.start", data.ID, c.IP(), c.Get(fiber.HeaderUserAgent)) return c.JSON(fiber.Map{ "display_name": user.Nick, "challenge": challenge, @@ -90,6 +91,7 @@ func doChallenge(c *fiber.Ctx) error { func exchangeToken(c *fiber.Ctx) error { var data struct { Code string `json:"code" form:"code"` + RefreshToken string `json:"refresh_token" form:"refresh_token"` ClientID string `json:"client_id" form:"client_id"` ClientSecret string `json:"client_secret" form:"client_secret"` RedirectUri string `json:"redirect_uri" form:"redirect_uri"` @@ -125,7 +127,7 @@ func exchangeToken(c *fiber.Ctx) error { }) case "refresh_token": // Refresh Token - access, refresh, err := security.RefreshToken(data.Code) + access, refresh, err := security.RefreshToken(data.RefreshToken) if err != nil { return fiber.NewError(fiber.StatusBadRequest, err.Error()) } diff --git a/pkg/server/oauth_api.go b/pkg/server/oauth_api.go index d4c0177..94b7a40 100644 --- a/pkg/server/oauth_api.go +++ b/pkg/server/oauth_api.go @@ -4,6 +4,7 @@ import ( "code.smartsheep.studio/hydrogen/passport/pkg/database" "code.smartsheep.studio/hydrogen/passport/pkg/models" "code.smartsheep.studio/hydrogen/passport/pkg/security" + "code.smartsheep.studio/hydrogen/passport/pkg/services" "github.com/gofiber/fiber/v2" "github.com/samber/lo" "strings" @@ -67,13 +68,12 @@ func doConnect(c *fiber.Ctx) error { switch response { case "code": // OAuth Authorization Mode - expired := time.Now().Add(7 * 24 * time.Hour) session, err := security.GrantOauthSession( user, client, strings.Split(scope, " "), []string{"Hydrogen.Passport", client.Alias}, - &expired, + nil, lo.ToPtr(time.Now()), c.IP(), c.Get(fiber.HeaderUserAgent), @@ -82,6 +82,7 @@ func doConnect(c *fiber.Ctx) error { if err != nil { return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } else { + services.AddEvent(user, "oauth.connect", client.Alias, c.IP(), c.Get(fiber.HeaderUserAgent)) return c.JSON(fiber.Map{ "session": session, "redirect_uri": redirect, @@ -89,13 +90,12 @@ func doConnect(c *fiber.Ctx) error { } case "token": // OAuth Implicit Mode - expired := time.Now().Add(7 * 24 * time.Hour) session, err := security.GrantOauthSession( user, client, strings.Split(scope, " "), []string{"Hydrogen.Passport", client.Alias}, - &expired, + nil, lo.ToPtr(time.Now()), c.IP(), c.Get(fiber.HeaderUserAgent), @@ -106,6 +106,7 @@ func doConnect(c *fiber.Ctx) error { } else if access, refresh, err := security.GetToken(session); err != nil { return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } else { + services.AddEvent(user, "oauth.connect", client.Alias, c.IP(), c.Get(fiber.HeaderUserAgent)) return c.JSON(fiber.Map{ "access_token": access, "refresh_token": refresh, diff --git a/pkg/server/startup.go b/pkg/server/startup.go index 8162283..2b0fe0f 100644 --- a/pkg/server/startup.go +++ b/pkg/server/startup.go @@ -25,6 +25,8 @@ func NewServer() { api := A.Group("/api").Name("API") { api.Get("/users/me", auth, getPrincipal) + api.Get("/users/me/events", auth, getEvents) + api.Post("/users", doRegister) api.Post("/users/me/confirm", doRegisterConfirm) diff --git a/pkg/services/events.go b/pkg/services/events.go new file mode 100644 index 0000000..c1a913d --- /dev/null +++ b/pkg/services/events.go @@ -0,0 +1,20 @@ +package services + +import ( + "code.smartsheep.studio/hydrogen/passport/pkg/database" + "code.smartsheep.studio/hydrogen/passport/pkg/models" +) + +func AddEvent(user models.Account, event, target, ip, ua string) models.ActionEvent { + evt := models.ActionEvent{ + Type: event, + Target: target, + IpAddress: ip, + UserAgent: ua, + AccountID: user.ID, + } + + database.C.Save(&evt) + + return evt +} diff --git a/view/src/layouts/RootLayout.tsx b/view/src/layouts/RootLayout.tsx index 563b686..475c644 100644 --- a/view/src/layouts/RootLayout.tsx +++ b/view/src/layouts/RootLayout.tsx @@ -18,7 +18,7 @@ export default function RootLayout(props: any) { if (ready()) { keepGate(location.pathname); } - }); + }, [ready, userinfo]); function keepGate(path: string, e?: BeforeLeaveEventArgs) { const whitelist = ["/auth/login", "/auth/register", "/users/me/confirm"]; diff --git a/view/src/pages/auth/login.tsx b/view/src/pages/auth/login.tsx index ed0c995..7d6b404 100644 --- a/view/src/pages/auth/login.tsx +++ b/view/src/pages/auth/login.tsx @@ -214,7 +214,7 @@ export default function LoginPage() {
- ); diff --git a/view/src/stores/userinfo.tsx b/view/src/stores/userinfo.tsx index 523e7d2..20bd54b 100644 --- a/view/src/stores/userinfo.tsx +++ b/view/src/stores/userinfo.tsx @@ -31,7 +31,7 @@ export async function refreshAtk() { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - code: rtk, + refresh_token: rtk, grant_type: "refresh_token" }) });