From 88d054811df785b92b1b76dd91265849af8f29b3 Mon Sep 17 00:00:00 2001
From: Runxi Yu <me@runxiyu.org>
Date: Wed, 12 Feb 2025 11:01:52 +0800
Subject: *: Migrate to the new path scheme

---
 handle_group_index.go              |   4 +-
 handle_repo_commit.go              |   4 +-
 handle_repo_index.go               |   4 +-
 handle_repo_log.go                 |   4 +-
 handle_repo_raw.go                 |   6 +--
 handle_repo_tree.go                |   6 +--
 main.go                            |  23 +++------
 resources.go                       |  26 ++++------
 router.go                          | 101 +++++++++++++++++++++++++++++++++++++
 templates/_footer.html.tmpl        |   2 +-
 templates/_head.html.tmpl          |   2 +-
 templates/_repo_header.html.tmpl   |   8 +--
 templates/index.html.tmpl          |   2 +-
 templates/repo_tree_file.html.tmpl |   2 +-
 url_misc.go                        |  20 ++++++++
 15 files changed, 162 insertions(+), 52 deletions(-)
 create mode 100644 router.go

diff --git a/handle_group_index.go b/handle_group_index.go
index f194cee..bc7a7f4 100644
--- a/handle_group_index.go
+++ b/handle_group_index.go
@@ -7,9 +7,9 @@ import (
 	"strings"
 )
 
-func handle_group_repos(w http.ResponseWriter, r *http.Request) {
+func handle_group_repos(w http.ResponseWriter, r *http.Request, params map[string]string) {
 	data := make(map[string]any)
-	group_name := r.PathValue("group_name")
+	group_name := params["group_name"]
 	data["group_name"] = group_name
 	entries, err := os.ReadDir(filepath.Join(config.Git.Root, group_name))
 	if err != nil {
diff --git a/handle_repo_commit.go b/handle_repo_commit.go
index 58c3992..aefd58b 100644
--- a/handle_repo_commit.go
+++ b/handle_repo_commit.go
@@ -16,9 +16,9 @@ type usable_file_patch struct {
 	Chunks []diff.Chunk
 }
 
-func handle_repo_commit(w http.ResponseWriter, r *http.Request) {
+func handle_repo_commit(w http.ResponseWriter, r *http.Request, params map[string]string) {
 	data := make(map[string]any)
-	group_name, repo_name, commit_id_specified_string := r.PathValue("group_name"), r.PathValue("repo_name"), r.PathValue("commit_id")
+	group_name, repo_name, commit_id_specified_string := params["group_name"], params["repo_name"], params["commit_id"]
 	data["group_name"], data["repo_name"] = group_name, repo_name
 	repo, err := open_git_repo(group_name, repo_name)
 	if err != nil {
diff --git a/handle_repo_index.go b/handle_repo_index.go
index af6a625..6372b03 100644
--- a/handle_repo_index.go
+++ b/handle_repo_index.go
@@ -4,9 +4,9 @@ import (
 	"net/http"
 )
 
-func handle_repo_index(w http.ResponseWriter, r *http.Request) {
+func handle_repo_index(w http.ResponseWriter, r *http.Request, params map[string]string) {
 	data := make(map[string]any)
-	group_name, repo_name := r.PathValue("group_name"), r.PathValue("repo_name")
+	group_name, repo_name := params["group_name"], params["repo_name"]
 	data["group_name"], data["repo_name"] = group_name, repo_name
 	repo, err := open_git_repo(group_name, repo_name)
 	if err != nil {
diff --git a/handle_repo_log.go b/handle_repo_log.go
index 67079c8..eff5859 100644
--- a/handle_repo_log.go
+++ b/handle_repo_log.go
@@ -7,9 +7,9 @@ import (
 )
 
 // TODO: I probably shouldn't include *all* commits here...
-func handle_repo_log(w http.ResponseWriter, r *http.Request) {
+func handle_repo_log(w http.ResponseWriter, r *http.Request, params map[string]string) {
 	data := make(map[string]any)
-	group_name, repo_name, ref_name := r.PathValue("group_name"), r.PathValue("repo_name"), r.PathValue("ref")
+	group_name, repo_name, ref_name := params["group_name"], params["repo_name"], params["ref"]
 	data["group_name"], data["repo_name"], data["ref"] = group_name, repo_name, ref_name
 	repo, err := open_git_repo(group_name, repo_name)
 	if err != nil {
diff --git a/handle_repo_raw.go b/handle_repo_raw.go
index 7ed3842..d335f6a 100644
--- a/handle_repo_raw.go
+++ b/handle_repo_raw.go
@@ -9,10 +9,10 @@ import (
 	"github.com/go-git/go-git/v5/plumbing/object"
 )
 
-func handle_repo_raw(w http.ResponseWriter, r *http.Request) {
+func handle_repo_raw(w http.ResponseWriter, r *http.Request, params map[string]string) {
 	data := make(map[string]any)
-	raw_path_spec := r.PathValue("rest")
-	group_name, repo_name, path_spec := r.PathValue("group_name"), r.PathValue("repo_name"), strings.TrimSuffix(raw_path_spec, "/")
+	raw_path_spec := params["rest"]
+	group_name, repo_name, path_spec := params["group_name"], params["repo_name"], strings.TrimSuffix(raw_path_spec, "/")
 
 	ref_type, ref_name, err := get_param_ref_and_type(r)
 	if err != nil {
diff --git a/handle_repo_tree.go b/handle_repo_tree.go
index 7ed6f1d..f95e945 100644
--- a/handle_repo_tree.go
+++ b/handle_repo_tree.go
@@ -14,10 +14,10 @@ import (
 	"github.com/go-git/go-git/v5/plumbing/object"
 )
 
-func handle_repo_tree(w http.ResponseWriter, r *http.Request) {
+func handle_repo_tree(w http.ResponseWriter, r *http.Request, params map[string]string) {
 	data := make(map[string]any)
-	raw_path_spec := r.PathValue("rest")
-	group_name, repo_name, path_spec := r.PathValue("group_name"), r.PathValue("repo_name"), strings.TrimSuffix(raw_path_spec, "/")
+	raw_path_spec := params["rest"]
+	group_name, repo_name, path_spec := params["group_name"], params["repo_name"], strings.TrimSuffix(raw_path_spec, "/")
 	ref_type, ref_name, err := get_param_ref_and_type(r)
 	if err != nil {
 		if errors.Is(err, err_no_ref_spec) {
diff --git a/main.go b/main.go
index 8c06a77..ea72819 100644
--- a/main.go
+++ b/main.go
@@ -26,27 +26,20 @@ func main() {
 		clog.Fatal(1, "Loading templates: "+err.Error())
 	}
 
-	err = serve_static()
-	if err != nil {
-		clog.Fatal(1, "Serving static: "+err.Error())
-	}
-
-	serve_source()
-
-	http.HandleFunc("/{$}", handle_index)
-	http.HandleFunc("/g/{group_name}/repos/{$}", handle_group_repos)
-	http.HandleFunc("/g/{group_name}/repos/{repo_name}/{$}", handle_repo_index)
-	http.HandleFunc("/g/{group_name}/repos/{repo_name}/tree/{rest...}", handle_repo_tree)
-	http.HandleFunc("/g/{group_name}/repos/{repo_name}/raw/{rest...}", handle_repo_raw)
-	http.HandleFunc("/g/{group_name}/repos/{repo_name}/log/{ref}/", handle_repo_log)
-	http.HandleFunc("/g/{group_name}/repos/{repo_name}/commit/{commit_id}", handle_repo_commit)
+// 	http.HandleFunc("/{$}", handle_index)
+// 	http.HandleFunc("/g/{group_name}/repos/{$}", handle_group_repos)
+// 	http.HandleFunc("/g/{group_name}/repos/{repo_name}/{$}", handle_repo_index)
+// 	http.HandleFunc("/g/{group_name}/repos/{repo_name}/tree/{rest...}", handle_repo_tree)
+// 	http.HandleFunc("/g/{group_name}/repos/{repo_name}/raw/{rest...}", handle_repo_raw)
+// 	http.HandleFunc("/g/{group_name}/repos/{repo_name}/log/{ref}/", handle_repo_log)
+// 	http.HandleFunc("/g/{group_name}/repos/{repo_name}/commit/{commit_id}", handle_repo_commit)
 
 	listener, err := net.Listen(config.HTTP.Net, config.HTTP.Addr)
 	if err != nil {
 		clog.Fatal(1, "Listening: "+err.Error())
 	}
 
-	err = http.Serve(listener, nil)
+	err = http.Serve(listener, &http_router_t{})
 	if err != nil {
 		clog.Fatal(1, "Serving: "+err.Error())
 	}
diff --git a/resources.go b/resources.go
index 30b0de5..4624603 100644
--- a/resources.go
+++ b/resources.go
@@ -13,12 +13,12 @@ import (
 //go:embed static/* templates/*
 var source_fs embed.FS
 
-func serve_source() {
-	http.Handle("/source/",
-		http.StripPrefix(
-			"/source/",
-			http.FileServer(http.FS(source_fs)),
-		),
+var source_handler http.Handler
+
+func init() {
+	source_handler = http.StripPrefix(
+		"/:/source/",
+		http.FileServer(http.FS(source_fs)),
 	)
 }
 
@@ -35,16 +35,12 @@ func load_templates() (err error) {
 	return err
 }
 
-func serve_static() (err error) {
+var static_handler http.Handler
+func init() {
 	static_fs, err := fs.Sub(resources_fs, "static")
 	if err != nil {
-		return err
+		panic(err)
 	}
-	http.Handle("/static/",
-		http.StripPrefix(
-			"/static/",
-			http.FileServer(http.FS(static_fs)),
-		),
-	)
-	return nil
+	static_handler = http.StripPrefix("/:/static/", http.FileServer(http.FS(static_fs)))
 }
+
diff --git a/router.go b/router.go
new file mode 100644
index 0000000..ed0e9b5
--- /dev/null
+++ b/router.go
@@ -0,0 +1,101 @@
+package main
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+)
+
+type http_router_t struct{}
+
+func (router *http_router_t) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	segments, _, err := parse_request_uri(r.RequestURI)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+
+	if segments[0] == ":" {
+		switch segments[1] {
+		case "static":
+			static_handler.ServeHTTP(w, r)
+		case "source":
+			source_handler.ServeHTTP(w, r)
+		default:
+			fmt.Fprintln(w, "Unknown system module type:", segments[1])
+		}
+		return
+	}
+
+	separator_index := -1
+	for i, part := range segments {
+		if part == ":" {
+			separator_index = i
+			break
+		}
+	}
+	non_empty_last_segments_len := len(segments)
+	dir_mode := false
+	if segments[len(segments)-1] == "" {
+		non_empty_last_segments_len--
+		dir_mode = true
+	}
+
+	params := make(map[string]string)
+	_ = params
+	switch {
+	case non_empty_last_segments_len == 0:
+		handle_index(w, r)
+	case separator_index == -1:
+		fmt.Fprintln(w, "Group indexing hasn't been implemented yet")
+	case non_empty_last_segments_len == separator_index+1:
+		fmt.Fprintln(w, "Group root hasn't been implemented yet")
+	case non_empty_last_segments_len == separator_index+2:
+		module_type := segments[separator_index+1]
+		params["group_name"] = segments[0]
+		switch module_type {
+		case "repos":
+			handle_group_repos(w, r, params)
+		default:
+			fmt.Fprintln(w, "Unknown module type:", module_type)
+		}
+	default:
+		module_type := segments[separator_index+1]
+		module_name := segments[separator_index+2]
+		params["group_name"] = segments[0]
+		switch module_type {
+		case "repos":
+			params["repo_name"] = module_name
+			// TODO: subgroups
+			if non_empty_last_segments_len == separator_index+3 {
+				if !dir_mode {
+					http.Redirect(w, r, r.URL.Path+"/", http.StatusSeeOther)
+					return
+				}
+				handle_repo_index(w, r, params)
+				return
+			}
+			repo_feature := segments[separator_index+3]
+			switch repo_feature {
+			case "tree":
+				params["rest"] = strings.Join(segments[separator_index+4:], "/")
+				handle_repo_tree(w, r, params)
+			case "raw":
+				params["rest"] = strings.Join(segments[separator_index+4:], "/")
+				handle_repo_raw(w, r, params)
+			case "log":
+				params["ref"] = segments[separator_index+4]
+				handle_repo_log(w, r, params)
+			case "commit":
+				params["commit_id"] = segments[separator_index+4]
+				handle_repo_commit(w, r, params)
+			}
+		default:
+			fmt.Fprintln(w, "Unknown module type:", module_type)
+		}
+	}
+}
+
+var err_bad_request = errors.New("Bad Request")
+
diff --git a/templates/_footer.html.tmpl b/templates/_footer.html.tmpl
index b7a9061..5b1acd2 100644
--- a/templates/_footer.html.tmpl
+++ b/templates/_footer.html.tmpl
@@ -1,3 +1,3 @@
 {{- define "footer" -}}
-<a href="https://lindenii.runxiyu.org/forge/">Lindenii Forge</a> (<a href="/source/">source</a>, <a href="https://forge.lindenii.runxiyu.org/g/lindenii/repos/forge/">upstream</a>)
+<a href="https://lindenii.runxiyu.org/forge/">Lindenii Forge</a> (<a href="/:/source/">source</a>, <a href="https://forge.lindenii.runxiyu.org/g/lindenii/repos/forge/">upstream</a>)
 {{- end -}}
diff --git a/templates/_head.html.tmpl b/templates/_head.html.tmpl
index b2cd487..4641107 100644
--- a/templates/_head.html.tmpl
+++ b/templates/_head.html.tmpl
@@ -1,5 +1,5 @@
 {{- define "head_common" -}}
 <meta charset="utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1" />
-<link rel="stylesheet" href="/static/style.css" />
+<link rel="stylesheet" href="/:/static/style.css" />
 {{- end -}}
diff --git a/templates/_repo_header.html.tmpl b/templates/_repo_header.html.tmpl
index 725d97b..8e5baa8 100644
--- a/templates/_repo_header.html.tmpl
+++ b/templates/_repo_header.html.tmpl
@@ -1,11 +1,11 @@
 {{- define "repo_header" -}}
 <a href="/">Lindenii Forge</a>
 /
-g
+<a href="/{{ .group_name }}/">{{ .group_name }}</a>
 /
-<a href="/g/{{ .group_name }}/">{{ .group_name }}</a>
+<a href="/{{ .group_name }}/:">:</a>
 /
-<a href="/g/{{ .group_name }}/repos/">repos</a>
+<a href="/{{ .group_name }}/:/repos/">repos</a>
 /
-<a href="/g/{{ .group_name }}/repos/{{ .repo_name }}/">{{ .repo_name }}</a>
+<a href="/{{ .group_name }}/:/repos/{{ .repo_name }}/">{{ .repo_name }}</a>
 {{- end -}}
diff --git a/templates/index.html.tmpl b/templates/index.html.tmpl
index de3a643..505ea70 100644
--- a/templates/index.html.tmpl
+++ b/templates/index.html.tmpl
@@ -13,7 +13,7 @@
 			<ul>
 				{{- range .groups }}
 					<li>
-						<a href="g/{{ . }}/repos/">{{ . }}</a>
+						<a href="{{ . }}/:/repos/">{{ . }}</a>
 					</li>
 				{{- end }}
 			</ul>
diff --git a/templates/repo_tree_file.html.tmpl b/templates/repo_tree_file.html.tmpl
index 90ce5ad..d4ba1e8 100644
--- a/templates/repo_tree_file.html.tmpl
+++ b/templates/repo_tree_file.html.tmpl
@@ -11,7 +11,7 @@
 			{{ template "repo_header" . }}
 		</header>
 		<p>
-			/{{ .path_spec }} (<a href="/g/{{ .group_name }}/repos/{{ .repo_name }}/raw/{{ .path_spec }}{{ if not (eq .ref_type "head") }}?{{ .ref_type }}={{ .ref }}{{ end }}">raw</a>)
+			/{{ .path_spec }} (<a href="/{{ .group_name }}/:/repos/{{ .repo_name }}/raw/{{ .path_spec }}{{ if not (eq .ref_type "head") }}?{{ .ref_type }}={{ .ref }}{{ end }}">raw</a>)
 		</p>
 		{{ .file_contents }}
 		<footer>
diff --git a/url_misc.go b/url_misc.go
index cbb30d6..e4bfd92 100644
--- a/url_misc.go
+++ b/url_misc.go
@@ -4,6 +4,9 @@ import (
 	"errors"
 	"net/http"
 	"net/url"
+	"strings"
+
+	"go.lindenii.runxiyu.org/lindenii-common/misc"
 )
 
 var (
@@ -40,3 +43,20 @@ func get_param_ref_and_type(r *http.Request) (ref_type, ref string, err error) {
 	}
 	return
 }
+
+func parse_request_uri(request_uri string) (segments []string, params url.Values, err error) {
+	path, params_string, _ := strings.Cut(request_uri, "?")
+
+	segments = strings.Split(strings.TrimPrefix(path, "/"), "/")
+
+	for i, segment := range segments {
+		segments[i], _ = url.QueryUnescape(segment)
+	}
+
+	params, err = url.ParseQuery(params_string)
+	if err != nil {
+		return nil, nil, misc.Wrap_one_error(err_bad_request, err)
+	}
+
+	return
+}
-- 
cgit v1.2.3