15 files changed,
287 insertions(+),
0 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-01-21 01:49:14 +0200
Authored at:
2026-01-17 23:00:41 +0200
Change ID:
spymymvukmmmwkrytuporvwntkkykzml
jump to
A
config.yml
路路路 1 +server: 2 + host: 0.0.0.0 3 + port: 8008 4 + 5 +meta: 6 + title: i like git 7 + description: hey kid, come get your free software 8 + host: git.olexsmir.xyz 9 + templates_dir: /home/olex/code/mugit/templates 10 + 11 +repo: 12 + dir: /home/olex/mugit-test/ 13 + readmes: 14 + - README 15 + - README.md 16 + - readme 17 + - readme.md 18 + - readme.txt 19 + masters: 20 + - master 21 + - main 22 + private: 23 + # repo also can be marked as private by: 24 + # - putting `muprivate` file in the root of repo 25 + # - adding `[mugit]\n private = true` 26 + - org
A
go.sum
路路路 1 +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 2 +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 3 +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 4 +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
A
internal/config/config.go
路路路 1 +package config 2 + 3 +import ( 4 + "fmt" 5 + "os" 6 + "path/filepath" 7 + 8 + "gopkg.in/yaml.v2" 9 +) 10 + 11 +type Config struct { 12 + Server struct { 13 + Host string `yaml:"host"` 14 + Port int `yaml:"port"` 15 + } `yaml:"server"` 16 + Meta struct { 17 + Title string `yaml:"title"` 18 + Description string `yaml:"description"` 19 + Host string `yaml:"host"` 20 + ChromaTheme string `yaml:"chroma_theme"` 21 + TemplatesDir string `yaml:"templates_dir"` 22 + } `yaml:"meta"` 23 + Repo struct { 24 + Dir string `yaml:"dir"` 25 + Readmes []string `yaml:"readmes"` 26 + Masters []string `yaml:"masters"` 27 + Private []string `yaml:"private"` 28 + } `yaml:"repo"` 29 + SSH struct { 30 + Keys []string `yaml:"keys"` 31 + } `yaml:"ssh"` 32 +} 33 + 34 +func Load(fpath string) (*Config, error) { 35 + configBytes, err := os.ReadFile(fpath) 36 + if err != nil { 37 + return nil, err 38 + } 39 + 40 + var config *Config 41 + if cerr := yaml.Unmarshal(configBytes, &config); cerr != nil { 42 + return nil, fmt.Errorf("parsing config: %w", cerr) 43 + } 44 + 45 + if config.Repo.Dir, err = filepath.Abs(config.Repo.Dir); err != nil { 46 + return nil, err 47 + } 48 + 49 + if config.Meta.TemplatesDir, err = filepath.Abs(config.Meta.TemplatesDir); err != nil { 50 + return nil, err 51 + } 52 + 53 + fmt.Println(config.Meta.TemplatesDir) 54 + 55 + if verr := config.validate(); verr != nil { 56 + return nil, verr 57 + } 58 + 59 + return config, nil 60 +} 61 + 62 +func (c Config) validate() error { 63 + // var errs []error 64 + // return errors.Join(errs...) 65 + return nil 66 +}
A
internal/handlers/handles.go
路路路 1 +package handlers 2 + 3 +import ( 4 + "html/template" 5 + "net/http" 6 + "path/filepath" 7 + 8 + "olexsmir.xyz/mugit/internal/config" 9 +) 10 + 11 +type handlers struct { 12 + c *config.Config 13 + t *template.Template 14 +} 15 + 16 +func InitRoutes(cfg *config.Config) *http.ServeMux { 17 + tmpls := template.Must(template.ParseGlob( 18 + filepath.Join(cfg.Meta.TemplatesDir, "*"), 19 + )) 20 + h := handlers{cfg, tmpls} 21 + 22 + mux := http.NewServeMux() 23 + mux.HandleFunc("GET /", h.index) 24 + 25 + return mux 26 +}
A
internal/handlers/routes.go
路路路 1 +package handlers 2 + 3 +import ( 4 + "log/slog" 5 + "net/http" 6 +) 7 + 8 +func (h *handlers) index(w http.ResponseWriter, r *http.Request) { 9 + data := make(map[string]any) 10 + data["meta"] = h.c.Meta 11 + 12 + w.WriteHeader(http.StatusOK) 13 + if err := h.t.ExecuteTemplate(w, "index", nil); err != nil { 14 + slog.Error("index template", "err", err) 15 + } 16 +}
A
internal/handlers/util.go
路路路 1 +package handlers 2 + 3 +import ( 4 + "log/slog" 5 + "net/http" 6 +) 7 + 8 +func (h *handlers) write404(w http.ResponseWriter) { 9 + w.WriteHeader(http.StatusNotFound) 10 + if err := h.t.ExecuteTemplate(w, "404", nil); err != nil { 11 + slog.Error("404 template", "err", err) 12 + } 13 +} 14 + 15 +func (h *handlers) write500(w http.ResponseWriter) { 16 + w.WriteHeader(http.StatusInternalServerError) 17 + if err := h.t.ExecuteTemplate(w, "500", nil); err != nil { 18 + slog.Error("500 template", "err", err) 19 + } 20 +}
A
main.go
路路路 1 +package main 2 + 3 +import ( 4 + "log" 5 + "log/slog" 6 + "net" 7 + "net/http" 8 + "os" 9 + "strconv" 10 + 11 + "olexsmir.xyz/mugit/internal/config" 12 + "olexsmir.xyz/mugit/internal/handlers" 13 +) 14 + 15 +func main() { 16 + if err := run(); err != nil { 17 + log.Fatalf("main: %s", err) 18 + os.Exit(1) 19 + } 20 +} 21 + 22 +func run() error { 23 + cfg, err := config.Load("/home/olex/code/mugit/config.yml") 24 + if err != nil { 25 + slog.Error("config error", "err", err) 26 + return err 27 + } 28 + 29 + mux := handlers.InitRoutes(cfg) 30 + 31 + port := strconv.Itoa(cfg.Server.Port) 32 + err = http.ListenAndServe(net.JoinHostPort(cfg.Server.Host, port), mux) 33 + if err != nil { 34 + slog.Error("server error", "err", err) 35 + } 36 + 37 + return nil 38 +}
A
templates/_head.html
路路路 1 +{{ define "head" }} 2 + <head> 3 + <meta charset="utf-8"> 4 + <meta name="viewport" content="width=device-width, initial-scale=1"> 5 + <link rel="stylesheet" href="/static/style.css" type="text/css"> 6 + <!-- TODO: icon --> 7 + {{ if .parent }} 8 + <title>{{ .meta.Title }} — {{ .name }} ({{ .ref }}): {{ .parent }}/</title> 9 + 10 + {{ else if .path }} 11 + <title>{{ .meta.Title }} — {{ .name }} ({{ .ref }}): {{ .path }}</title> 12 + {{ else if .files }} 13 + <title>{{ .meta.Title }} — {{ .name }} ({{ .ref }})</title> 14 + {{ else if .commit }} 15 + <title>{{ .meta.Title }} — {{ .name }}: {{ .commit.This }}</title> 16 + {{ else if .branches }} 17 + <title>{{ .meta.Title }} — {{ .name }}: refs</title> 18 + {{ else if .commits }} 19 + {{ if .log }} 20 + <title>{{ .meta.Title }} — {{ .name }}: log</title> 21 + {{ else }} 22 + <title>{{ .meta.Title }} — {{ .name }}</title> 23 + {{ end }} 24 + {{ else }} 25 + <title>{{ .meta.Title }}</title> 26 + {{ end }} 27 + <!-- {{ if and .servername .gomod }} --> 28 + <!-- <meta name="go-import" content="{{ .servername}}/{{ .name }} git https://{{ .servername }}/{{ .name }}"> --> 29 + <!-- {{ end }} --> 30 + </head> 31 +{{ end }}