package main
import (
"flag"
"log"
"net/http"
"os/user"
"path/filepath"
"strings"
"time"
"git-browser/internal/repository"
"git-browser/internal/web"
)
func main() {
cloneUser := flag.String("clone-user", "git", "SSH user allowed to access repositories")
cloneHost := flag.String("clone-host", "", "hostname used for generating clone URLs (defaults to the request host)")
cloneRoot := flag.String("clone-root", "", "repositories location root used for generating clone URLs (relative to the ssh user's home directory)")
root := flag.String("root", "repos", "where the actual repositories are stored (absolute path or relative to the current user's home directory)")
hide := flag.String("hide", "", "semicolon-separated list of repositories to hide from the UI")
listen := flag.String("listen", ":8080", "HTTP listen address")
flag.Parse()
current, err := user.Current()
if err != nil {
log.Fatalf("resolve current user: %v", err)
}
resolvedRoot, defaultCloneRoot, err := resolveRoot(current.Username, *root)
if err != nil {
log.Fatalf("resolve root: %v", err)
}
if *cloneRoot == "" {
*cloneRoot = defaultCloneRoot
}
store, err := repository.Discover(resolvedRoot, parseHiddenRepositories(*hide)...)
if err != nil {
log.Fatalf("discover repositories: %v", err)
}
stopRefresh := store.StartPeriodicRefresh(5*time.Minute, func(err error) {
log.Printf("refresh repositories: %v", err)
})
defer stopRefresh()
server, err := web.NewServer(store, web.Config{
CloneUser: *cloneUser,
CloneHost: *cloneHost,
CloneRoot: *cloneRoot,
})
if err != nil {
log.Fatalf("create server: %v", err)
}
log.Printf("serving %s on %s", store.Root(), *listen)
if err := http.ListenAndServe(*listen, server.Handler()); err != nil {
log.Fatal(err)
}
}
func resolveRoot(username, root string) (string, string, error) {
account, err := user.Lookup(username)
if err != nil {
return "", "", err
}
trimmed := strings.TrimSpace(root)
if trimmed == "" {
return account.HomeDir, "", nil
}
if filepath.IsAbs(trimmed) {
return trimmed, "", nil
}
return filepath.Join(account.HomeDir, filepath.FromSlash(trimmed)), filepath.ToSlash(trimmed), nil
}
func parseHiddenRepositories(raw string) []string {
parts := strings.Split(raw, ";")
hidden := make([]string, 0, len(parts))
for _, part := range parts {
name := strings.TrimSpace(part)
if name == "" {
continue
}
hidden = append(hidden, name)
}
return hidden
}