+13
-13
cmd/knotserver/main.go
+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
+5
git/git.go
+1
go.mod
+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/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
+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
+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
+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
+5
knotserver/git/git.go
+5
-7
knotserver/handler.go
+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
+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")