mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 12:33:16 -05:00
feat(github): allow classic tokens and any-repository lists, both opt-in
The GitHub add-on refused classic tokens in the page only, and its repository list took exact owner/name entries. A classic token (ghp_) is now an explicit choice of the connection. The kind declares a token guard: the host refuses a token with that prefix unless the connection sets the option classicToken to allow, and the request that turns it on must carry confirm_wider_token. It is checked when the token is saved and again each time it is used, so switching the option off stops a stored token. The list and the write mode still apply. The page shows a warning and two ticks. The list takes owner/* and *. The host matches them before the add-on runs, without regard to case and on whole names, so other-owner/x and owner-evil/x are not covered by acme/*, and a key that holds a star is never a name. A kind opts in with wildcardResources; only GitHub does, so the other add-ons keep exact matching. Saving * needs confirm_any_resource, and the write mode stays separate. A read tool that declares enumerates (the new list-repos) runs only for a list of * and asks GitHub which repositories the token reaches. Assisted-by: Claude:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
38 files changed
+1296
-94
No files matched your search
@@ -241,6 +241,8 @@ func (s *Server) serve(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case path == "user":
|
||||
writeJSON(w, map[string]any{"login": login, "id": 4242, "type": "User"})
|
||||
case path == "user/repos":
|
||||
s.userRepos(w, r)
|
||||
case path == "search/code":
|
||||
s.searchCode(w, r)
|
||||
case strings.HasPrefix(path, "repos/"):
|
||||
@@ -686,3 +688,37 @@ func writeJSON(w http.ResponseWriter, v any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
// userRepos answers GET /user/repos: every repository of the fake, sorted by
|
||||
// name, one page at a time. The fake knows no ownership, so all are visible.
|
||||
func (s *Server) userRepos(w http.ResponseWriter, r *http.Request) {
|
||||
s.mu.Lock()
|
||||
var names []string
|
||||
for _, repo := range s.repos {
|
||||
names = append(names, repo.FullName)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
sort.Strings(names)
|
||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||
per, _ := strconv.Atoi(r.URL.Query().Get("per_page"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if per < 1 {
|
||||
per = 30
|
||||
}
|
||||
start := min((page-1)*per, len(names))
|
||||
end := min(start+per, len(names))
|
||||
if end < len(names) {
|
||||
w.Header().Set("Link", `<https://api.github.com/user/repos?page=`+strconv.Itoa(page+1)+`>; rel="next"`)
|
||||
}
|
||||
out := []map[string]any{}
|
||||
for _, n := range names[start:end] {
|
||||
s.mu.Lock()
|
||||
repo := s.repos[strings.ToLower(n)]
|
||||
s.mu.Unlock()
|
||||
out = append(out, map[string]any{"full_name": repo.FullName, "default_branch": repo.DefaultBranch, "private": repo.Private, "archived": false,
|
||||
"html_url": "https://github.com/" + repo.FullName, "permissions": map[string]bool{"pull": true, "push": true}})
|
||||
}
|
||||
writeJSON(w, out)
|
||||
}
|
||||
Reference in new issue
Block a user