this repo has no description

knotserver: better config management and /repo/new

Changed files
+136 -64
cmd
knotserver
git
knotserver
+13 -13
cmd/knotserver/main.go
··· 1 package main 2 3 import ( 4 - "flag" 5 "fmt" 6 "log" 7 "log/slog" 8 "net/http" 9 "os" 10 11 - "github.com/icyphox/bild/config" 12 - "github.com/icyphox/bild/db" 13 "github.com/icyphox/bild/knotserver" 14 ) 15 16 func main() { 17 - var cfg string 18 - flag.StringVar(&cfg, "config", "./config.yaml", "path to config file") 19 - flag.Parse() 20 21 slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil))) 22 23 - c, err := config.Read(cfg) 24 if err != nil { 25 log.Fatal(err) 26 } 27 - db, err := db.Setup(c.Server.DBPath) 28 - if err != nil { 29 - log.Fatalf("failed to setup db: %s", err) 30 - } 31 32 - mux, err := knotserver.Setup(c, db) 33 if err != nil { 34 log.Fatal(err) 35 } 36 37 - addr := fmt.Sprintf("%s:%d", c.Server.Host, c.Server.Port) 38 39 log.Println("starting main server on", addr) 40 log.Fatal(http.ListenAndServe(addr, mux))
··· 1 package main 2 3 import ( 4 + "context" 5 "fmt" 6 "log" 7 "log/slog" 8 "net/http" 9 "os" 10 + "os/signal" 11 + "syscall" 12 13 "github.com/icyphox/bild/knotserver" 14 + "github.com/icyphox/bild/knotserver/config" 15 ) 16 17 func main() { 18 + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) 19 + defer stop() 20 21 slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil))) 22 23 + c, err := config.Load(ctx) 24 if err != nil { 25 log.Fatal(err) 26 } 27 + // db, err := db.Setup(c.Server.DBPath) 28 + // if err != nil { 29 + // log.Fatalf("failed to setup db: %s", err) 30 + // } 31 32 + mux, err := knotserver.Setup(c, nil) 33 if err != nil { 34 log.Fatal(err) 35 } 36 37 + addr := fmt.Sprintf("%s:%d", c.Host, c.Port) 38 39 log.Println("starting main server on", addr) 40 log.Fatal(http.ListenAndServe(addr, mux))
+5
git/git.go
··· 202 } 203 204 func (g *GitRepo) FindMainBranch(branches []string) (string, error) { 205 for _, b := range branches { 206 _, err := g.r.ResolveRevision(plumbing.Revision(b)) 207 if err == nil {
··· 202 } 203 204 func (g *GitRepo) FindMainBranch(branches []string) (string, error) { 205 + branches = append(branches, []string{ 206 + "main", 207 + "master", 208 + "trunk", 209 + }...) 210 for _, b := range branches { 211 _, err := g.r.ResolveRevision(plumbing.Revision(b)) 212 if err == nil {
+1
go.mod
··· 17 github.com/mattn/go-sqlite3 v1.14.24 18 github.com/microcosm-cc/bluemonday v1.0.27 19 github.com/russross/blackfriday/v2 v2.1.0 20 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e 21 golang.org/x/crypto v0.31.0 22 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
··· 17 github.com/mattn/go-sqlite3 v1.14.24 18 github.com/microcosm-cc/bluemonday v1.0.27 19 github.com/russross/blackfriday/v2 v2.1.0 20 + github.com/sethvargo/go-envconfig v1.1.0 21 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e 22 golang.org/x/crypto v0.31.0 23 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
+2
go.sum
··· 210 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 211 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 212 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 213 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 214 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 215 github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
··· 210 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 211 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 212 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 213 + github.com/sethvargo/go-envconfig v1.1.0 h1:cWZiJxeTm7AlCvzGXrEXaSTCNgip5oJepekh/BOQuog= 214 + github.com/sethvargo/go-envconfig v1.1.0/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/6nRidxI8YvGiHw= 215 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 216 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 217 github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
+49
knotserver/config/config.go
···
··· 1 + package config 2 + 3 + import ( 4 + "context" 5 + 6 + "github.com/sethvargo/go-envconfig" 7 + ) 8 + 9 + type Repo struct { 10 + ScanPath string `env:"SCAN_PATH, default=/home/git"` 11 + Readme []string `env:"README"` 12 + MainBranch []string `env:"MAIN_BRANCH"` 13 + } 14 + 15 + type Config struct { 16 + Host string `env:"KNOTSERVER_HOST, default=0.0.0.0"` 17 + Port int `env:"KNOTSERVER_PORT, default=5555"` 18 + Secret string `env:"KNOTSERVER_SECRET, required"` 19 + 20 + Repo Repo `env:",prefix=KNOTSERVER_REPO_"` 21 + } 22 + 23 + func Load(ctx context.Context) (*Config, error) { 24 + var cfg Config 25 + err := envconfig.Process(ctx, &cfg) 26 + if err != nil { 27 + return nil, err 28 + } 29 + 30 + if cfg.Repo.Readme == nil { 31 + cfg.Repo.Readme = []string{ 32 + "README.md", "readme.md", 33 + "README", 34 + "readme", 35 + "README.markdown", 36 + "readme.markdown", 37 + "README.txt", 38 + "readme.txt", 39 + "README.rst", 40 + "readme.rst", 41 + "README.org", 42 + "readme.org", 43 + "README.asciidoc", 44 + "readme.asciidoc", 45 + } 46 + } 47 + 48 + return &cfg, nil 49 + }
+1 -3
knotserver/file.go
··· 7 "net/http" 8 "strings" 9 10 - "github.com/icyphox/bild/git" 11 ) 12 13 func (h *Handle) listFiles(files []git.NiceTree, data map[string]any, w http.ResponseWriter) { 14 data["files"] = files 15 - data["meta"] = h.c.Meta 16 17 writeJSON(w, data) 18 return ··· 60 61 data["linecount"] = lines 62 data["content"] = content 63 - data["meta"] = h.c.Meta 64 65 writeJSON(w, data) 66 return
··· 7 "net/http" 8 "strings" 9 10 + "github.com/icyphox/bild/knotserver/git" 11 ) 12 13 func (h *Handle) listFiles(files []git.NiceTree, data map[string]any, w http.ResponseWriter) { 14 data["files"] = files 15 16 writeJSON(w, data) 17 return ··· 59 60 data["linecount"] = lines 61 data["content"] = content 62 63 writeJSON(w, data) 64 return
+5
knotserver/git/git.go
··· 202 } 203 204 func (g *GitRepo) FindMainBranch(branches []string) (string, error) { 205 for _, b := range branches { 206 _, err := g.r.ResolveRevision(plumbing.Revision(b)) 207 if err == nil {
··· 202 } 203 204 func (g *GitRepo) FindMainBranch(branches []string) (string, error) { 205 + branches = append(branches, []string{ 206 + "main", 207 + "master", 208 + "trunk", 209 + }...) 210 for _, b := range branches { 211 _, err := g.r.ResolveRevision(plumbing.Revision(b)) 212 if err == nil {
+5 -7
knotserver/handler.go
··· 5 "net/http" 6 7 "github.com/go-chi/chi/v5" 8 - "github.com/icyphox/bild/config" 9 "github.com/icyphox/bild/db" 10 ) 11 12 func Setup(c *config.Config, db *db.DB) (http.Handler, error) { ··· 16 c: c, 17 db: db, 18 } 19 - 20 - // r.Route("/repo", func(r chi.Router) { 21 - // r.Use(h.AuthMiddleware) 22 - // r.Get("/new", h.NewRepo) 23 - // r.Put("/new", h.NewRepo) 24 - // }) 25 26 r.Get("/", h.Index) 27 r.Route("/{did}", func(r chi.Router) { ··· 44 r.Get("/commit/{ref}", h.Diff) 45 r.Get("/refs/", h.Refs) 46 }) 47 }) 48 49 return r, nil
··· 5 "net/http" 6 7 "github.com/go-chi/chi/v5" 8 "github.com/icyphox/bild/db" 9 + "github.com/icyphox/bild/knotserver/config" 10 ) 11 12 func Setup(c *config.Config, db *db.DB) (http.Handler, error) { ··· 16 c: c, 17 db: db, 18 } 19 20 r.Get("/", h.Index) 21 r.Route("/{did}", func(r chi.Router) { ··· 38 r.Get("/commit/{ref}", h.Diff) 39 r.Get("/refs/", h.Refs) 40 }) 41 + }) 42 + 43 + r.Route("/repo", func(r chi.Router) { 44 + r.Put("/new", h.NewRepo) 45 }) 46 47 return r, nil
+55 -41
knotserver/routes.go
··· 2 3 import ( 4 "compress/gzip" 5 "errors" 6 "fmt" 7 "html/template" ··· 13 14 "github.com/go-chi/chi/v5" 15 "github.com/go-git/go-git/v5/plumbing" 16 - "github.com/icyphox/bild/git" 17 "github.com/russross/blackfriday/v2" 18 ) 19 ··· 84 data["readme"] = readmeContent 85 data["commits"] = commits 86 data["desc"] = getDescription(path) 87 - data["servername"] = h.c.Server.Name 88 - data["meta"] = h.c.Meta 89 90 writeJSON(w, data) 91 return ··· 218 return 219 } 220 221 data := make(map[string]interface{}) 222 data["commits"] = commits 223 - data["meta"] = h.c.Meta 224 data["ref"] = ref 225 data["desc"] = getDescription(path) 226 data["log"] = true 227 228 writeJSON(w, data) 229 return ··· 251 data["commit"] = diff.Commit 252 data["stat"] = diff.Stat 253 data["diff"] = diff.Diff 254 - data["meta"] = h.c.Meta 255 data["ref"] = ref 256 data["desc"] = getDescription(path) 257 ··· 282 283 data := make(map[string]interface{}) 284 285 - data["meta"] = h.c.Meta 286 data["branches"] = branches 287 data["tags"] = tags 288 data["desc"] = getDescription(path) ··· 291 return 292 } 293 294 - func (h *Handle) ServeStatic(w http.ResponseWriter, r *http.Request) { 295 - f := chi.URLParam(r, "file") 296 - f = filepath.Clean(filepath.Join(h.c.Dirs.Static, f)) 297 - 298 - http.ServeFile(w, r, f) 299 - } 300 - 301 // func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) { 302 // session, _ := h.s.Get(r, "bild-session") 303 // did := session.Values["did"].(string) ··· 361 // } 362 // } 363 364 - // func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) { 365 - // session, _ := h.s.Get(r, "bild-session") 366 - // did := session.Values["did"].(string) 367 - // handle := session.Values["handle"].(string) 368 369 - // switch r.Method { 370 - // case http.MethodGet: 371 - // if err := h.t.ExecuteTemplate(w, "repo/new", nil); err != nil { 372 - // log.Println(err) 373 - // return 374 - // } 375 - // case http.MethodPut: 376 - // name := r.FormValue("name") 377 - // description := r.FormValue("description") 378 379 - // repoPath := filepath.Join(h.c.Repo.ScanPath, did, name) 380 - // err := git.InitBare(repoPath) 381 - // if err != nil { 382 - // h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.") 383 - // return 384 - // } 385 386 - // err = h.db.AddRepo(did, name, description) 387 - // if err != nil { 388 - // h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.") 389 - // return 390 - // } 391 392 - // w.Header().Set("HX-Redirect", fmt.Sprintf("/@%s/%s", handle, name)) 393 - // w.WriteHeader(http.StatusOK) 394 - // } 395 - // } 396 397 // func (h *Handle) Timeline(w http.ResponseWriter, r *http.Request) { 398 // session, err := h.s.Get(r, "bild-session")
··· 2 3 import ( 4 "compress/gzip" 5 + "encoding/json" 6 "errors" 7 "fmt" 8 "html/template" ··· 14 15 "github.com/go-chi/chi/v5" 16 "github.com/go-git/go-git/v5/plumbing" 17 + "github.com/go-git/go-git/v5/plumbing/object" 18 + "github.com/icyphox/bild/knotserver/git" 19 "github.com/russross/blackfriday/v2" 20 ) 21 ··· 86 data["readme"] = readmeContent 87 data["commits"] = commits 88 data["desc"] = getDescription(path) 89 90 writeJSON(w, data) 91 return ··· 218 return 219 } 220 221 + // Get page parameters 222 + page := 1 223 + pageSize := 30 224 + 225 + if pageParam := r.URL.Query().Get("page"); pageParam != "" { 226 + if p, err := strconv.Atoi(pageParam); err == nil && p > 0 { 227 + page = p 228 + } 229 + } 230 + 231 + if pageSizeParam := r.URL.Query().Get("per_page"); pageSizeParam != "" { 232 + if ps, err := strconv.Atoi(pageSizeParam); err == nil && ps > 0 { 233 + pageSize = ps 234 + } 235 + } 236 + 237 + // Calculate pagination 238 + start := (page - 1) * pageSize 239 + end := start + pageSize 240 + total := len(commits) 241 + 242 + if start >= total { 243 + commits = []*object.Commit{} 244 + } else { 245 + if end > total { 246 + end = total 247 + } 248 + commits = commits[start:end] 249 + } 250 + 251 data := make(map[string]interface{}) 252 data["commits"] = commits 253 data["ref"] = ref 254 data["desc"] = getDescription(path) 255 data["log"] = true 256 + data["total"] = total 257 + data["page"] = page 258 + data["per_page"] = pageSize 259 260 writeJSON(w, data) 261 return ··· 283 data["commit"] = diff.Commit 284 data["stat"] = diff.Stat 285 data["diff"] = diff.Diff 286 data["ref"] = ref 287 data["desc"] = getDescription(path) 288 ··· 313 314 data := make(map[string]interface{}) 315 316 data["branches"] = branches 317 data["tags"] = tags 318 data["desc"] = getDescription(path) ··· 321 return 322 } 323 324 // func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) { 325 // session, _ := h.s.Get(r, "bild-session") 326 // did := session.Values["did"].(string) ··· 384 // } 385 // } 386 387 + func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) { 388 + data := struct { 389 + DID string `json:"did"` 390 + Name string `json:"name"` 391 + }{} 392 393 + if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 394 + writeError(w, "invalid request body", http.StatusBadRequest) 395 + return 396 + } 397 398 + did := data.DID 399 + name := data.Name 400 401 + repoPath := filepath.Join(h.c.Repo.ScanPath, did, name) 402 + err := git.InitBare(repoPath) 403 + if err != nil { 404 + writeError(w, err.Error(), http.StatusInternalServerError) 405 + return 406 + } 407 408 + w.WriteHeader(http.StatusNoContent) 409 + } 410 411 // func (h *Handle) Timeline(w http.ResponseWriter, r *http.Request) { 412 // session, err := h.s.Get(r, "bild-session")