From e2d29f9dc5046e08708989ebd7b5c1f7a49eca52 Mon Sep 17 00:00:00 2001 From: poyo Date: Sat, 11 Jul 2026 22:38:39 +0200 Subject: [PATCH] config + get hackatime projects --- app/repo-config.go | 48 +++++++++++++++++++++++++++++++++++++++++ app/repo.go | 47 ++++++++++++++++++++++++---------------- lib/hackatime/fetch.go | 45 +++++++++++++++++++++++++++++++++----- views/layouts/repo.html | 7 ++++-- views/repo/config.html | 14 ++++++++++++ 5 files changed, 136 insertions(+), 25 deletions(-) create mode 100644 app/repo-config.go create mode 100644 views/repo/config.html diff --git a/app/repo-config.go b/app/repo-config.go new file mode 100644 index 0000000..ee11dd5 --- /dev/null +++ b/app/repo-config.go @@ -0,0 +1,48 @@ +package app + +import ( + md "farga/app/middleware" + "farga/db/types" + "farga/lib/git" + hk "farga/lib/hackatime" + "net/http" +) + +func (a *App) RepoConfigHandler(w http.ResponseWriter, r *http.Request) { + userName := r.PathValue("user") + repoName := r.PathValue("repo") + + dbRepo, err := a.DB.GetRepoByHandle(userName, repoName) + if err != nil { + http.Error(w, "Repo not found", 404) + return + } + + reqUser := r.Context().Value(md.UserKey).(*types.User) + if reqUser.ID != *dbRepo.UserID { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + + var projects []hk.Project + if dbRepo.User.HackatimeToken != "" { + projects, err = hk.GetProjects(dbRepo.User.HackatimeToken) + if err != nil { + http.Error(w, "Something went wrong", 500) + return + } + } + + gitRepo, err := git.OpenRepo(dbRepo.UserID, &dbRepo.ID, "") + if err != nil { + http.Error(w, "Something went wrong", 500) + return + } + + a.renderRepo(w, r, "config", repoViewOptions{ + View: "config", + DB: &dbRepo, + Git: gitRepo, + Projects: projects, + }) +} diff --git a/app/repo.go b/app/repo.go index 70a22c0..0c52198 100644 --- a/app/repo.go +++ b/app/repo.go @@ -6,6 +6,7 @@ import ( "farga/db/types" "farga/lib" "farga/lib/git" + hk "farga/lib/hackatime" "farga/lib/mime" "fmt" "io" @@ -60,35 +61,45 @@ func (a *App) RepoRoutes() *http.ServeMux { mux.HandleFunc("/{user}/{repo}/log", withMode("log", a.RepoHandler)) mux.HandleFunc("/{user}/{repo}/log/{branch}", withMode("log", a.RepoHandler)) + mux.Handle("/{user}/{repo}/config", md.RequireAuth(http.HandlerFunc(a.RepoConfigHandler), a.DB)) + return mux } var availableTypes = []string{"tree", "blob", "raw", "log"} type repoViewOptions struct { - View string - Git *git.GitRepo - DB *types.Repo - Path string - Blob *git.File - Log *git.Log - Issues []types.Issue - Issue types.Issue + View string + Git *git.GitRepo + DB *types.Repo + Path string + Blob *git.File + Log *git.Log + Issues []types.Issue + Issue types.Issue + Projects []hk.Project } // little wrapper because I was scared of missing something func (a *App) renderRepo(w http.ResponseWriter, r *http.Request, template string, o repoViewOptions) { + user := r.Context().Value(md.UserKey) + isOwner := false + if user != nil { + isOwner = true + } var options = map[string]any{ - "Meta": o.DB, - "Blob": o.Blob, - "Log": o.Log, - "Git": o.Git, - "Owner": o.DB.User, - "Path": o.Path, - "Parent": filepath.Dir(strings.TrimSuffix(o.Path, "/")), - "View": o.View, - "Issues": o.Issues, - "Issue": o.Issue, + "Meta": o.DB, + "Blob": o.Blob, + "Log": o.Log, + "Git": o.Git, + "Owner": o.DB.User, + "Path": o.Path, + "Parent": filepath.Dir(strings.TrimSuffix(o.Path, "/")), + "View": o.View, + "Issues": o.Issues, + "Issue": o.Issue, + "IsOwner": isOwner, + "Projects": o.Projects, } // I realized I can get almost all the data I need from those 4 objects lmao. // like yeah, obviously, but I was asking for too many things before xD diff --git a/lib/hackatime/fetch.go b/lib/hackatime/fetch.go index 0f6c351..31bd411 100644 --- a/lib/hackatime/fetch.go +++ b/lib/hackatime/fetch.go @@ -7,17 +7,23 @@ import ( "net/http" ) +type Project struct { + Name string `json:"name"` + TotalSeconds uint `json:"total_seconds"` +} + +type ProjectResponse struct { + Projects []Project `json:"projects"` +} + type User struct { ID uint `json:"id"` SlackID string `json:"slack_id"` Emails []string `json:"emails"` } -// get a user from their bearer/secret hackatime token. -func GetUserInfo(bearer string) (*User, error) { - requestUrl := "https://hackatime.hackclub.com/api/v1/authenticated/me" - - req, err := http.NewRequest("GET", requestUrl, nil) +func fetch(url string, bearer string) ([]byte, error) { + req, err := http.NewRequest("GET", url, nil) if err != nil { fmt.Println(err) @@ -42,8 +48,37 @@ func GetUserInfo(bearer string) (*User, error) { return nil, err } + return body, err +} + +// get a user from their bearer/secret hackatime token. +func GetUserInfo(bearer string) (*User, error) { + requestUrl := "https://hackatime.hackclub.com/api/v1/authenticated/me" + + body, err := fetch(requestUrl, bearer) + if err != nil { + fmt.Println(err) + return nil, err + } + var user User err = json.Unmarshal(body, &user) return &user, nil } + +// gets all projects from a given user +func GetProjects(bearer string) ([]Project, error) { + requestUrl := "https://hackatime.hackclub.com/api/v1/authenticated/projects" + + body, err := fetch(requestUrl, bearer) + if err != nil { + fmt.Println(err) + return nil, err + } + + var projects ProjectResponse + err = json.Unmarshal(body, &projects) + + return projects.Projects, err +} diff --git a/views/layouts/repo.html b/views/layouts/repo.html index ec2ce48..79f333e 100644 --- a/views/layouts/repo.html +++ b/views/layouts/repo.html @@ -26,11 +26,14 @@ diff --git a/views/repo/config.html b/views/repo/config.html new file mode 100644 index 0000000..3552116 --- /dev/null +++ b/views/repo/config.html @@ -0,0 +1,14 @@ +{{define "content"}} +

Hackatime.

+{{if .user.HackatimeToken}} +
+ +
+{{else}} +

You've got to link your Hackatime before you link a project.

+{{end}} +{{end}} \ No newline at end of file -- 2.54.0