diff options
author | Runxi Yu <me@runxiyu.org> | 2025-03-06 22:01:08 +0800 |
---|---|---|
committer | Runxi Yu <me@runxiyu.org> | 2025-03-06 22:01:31 +0800 |
commit | fa62d8eae273d89937d65d6a294f028e1ce22d88 (patch) | |
tree | 226d5fab65927ef977b664a35e6a2b2f30169440 | |
parent | group: Add description field to the create repo form (diff) | |
download | forge-fa62d8eae273d89937d65d6a294f028e1ce22d88.tar.gz forge-fa62d8eae273d89937d65d6a294f028e1ce22d88.tar.zst forge-fa62d8eae273d89937d65d6a294f028e1ce22d88.zip |
group/index: Allow repo creation via web
-rw-r--r-- | config.go | 3 | ||||
-rw-r--r-- | forge.scfg | 4 | ||||
-rw-r--r-- | http_handle_group_index.go | 56 | ||||
-rw-r--r-- | static/style.css | 16 | ||||
-rw-r--r-- | templates/group.tmpl | 11 | ||||
-rw-r--r-- | url.go | 15 |
6 files changed, 105 insertions, 0 deletions
@@ -28,6 +28,9 @@ var config struct { Socket string `scfg:"socket"` Execs string `scfg:"execs"` } `scfg:"hooks"` + Git struct { + RepoDir string `scfg:"repo_dir"` + } `scfg:"git"` SSH struct { Net string `scfg:"net"` Addr string `scfg:"addr"` @@ -15,6 +15,10 @@ http { root https://forge.example.org } +git { + repo_dir /var/lib/lindenii/forge/repos +} + ssh { # What network transport should we listen on? # This should be "tcp" in almost all cases. diff --git a/http_handle_group_index.go b/http_handle_group_index.go index 97ffee5..edb1368 100644 --- a/http_handle_group_index.go +++ b/http_handle_group_index.go @@ -5,6 +5,8 @@ package main import ( "net/http" + "path/filepath" + "strconv" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" @@ -74,6 +76,60 @@ func handle_group_index(w http.ResponseWriter, r *http.Request, params map[strin } direct_access := (count > 0) + if r.Method == "POST" { + if !direct_access { + http.Error(w, "You do not have direct access to this group", http.StatusForbidden) + return + } + + repo_name := r.FormValue("repo_name") + repo_description := r.FormValue("repo_description") + contrib_requirements := r.FormValue("repo_contrib") + if repo_name == "" { + http.Error(w, "Repo name is required", http.StatusBadRequest) + return + } + + var new_repo_id int + err := database.QueryRow( + r.Context(), + `INSERT INTO repos (name, description, group_id, contrib_requirements) + VALUES ($1, $2, $3, $4) + RETURNING id`, + repo_name, + repo_description, + group_id, + contrib_requirements, + ).Scan(&new_repo_id) + if err != nil { + http.Error(w, "Error creating repo: "+err.Error(), http.StatusInternalServerError) + return + } + + file_path := filepath.Join(config.Git.RepoDir, strconv.Itoa(new_repo_id)+".git") + + _, err = database.Exec( + r.Context(), + `UPDATE repos + SET filesystem_path = $1 + WHERE id = $2`, + file_path, + new_repo_id, + ) + if err != nil { + http.Error(w, "Error updating repo path: "+err.Error(), http.StatusInternalServerError) + return + } + + if err = git_bare_init_with_default_hooks(file_path); err != nil { + http.Error(w, "Error initializing repo: "+err.Error(), http.StatusInternalServerError) + return + } + + redirect_unconditionally(w, r) + return + } + // Repos var rows pgx.Rows rows, err = database.Query(r.Context(), ` diff --git a/static/style.css b/static/style.css index 5e35cd1..0f53c86 100644 --- a/static/style.css +++ b/static/style.css @@ -271,6 +271,7 @@ input[type=password] { } td.tdinput, th.tdinput { padding: 0; + position: relative; } td.tdinput textarea, td.tdinput input[type=text], @@ -280,6 +281,21 @@ th.tdinput input[type=text], th.tdinput input[type=password] { background-color: transparent; } +td.tdinput select { + position: absolute; + background-color: var(--background-color); + border: none; + /* + width: 100%; + height: 100%; + */ + box-sizing: border-box; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + /* Button styles */ .btn-primary, a.btn-primary { diff --git a/templates/group.tmpl b/templates/group.tmpl index ae0892c..5f3f0da 100644 --- a/templates/group.tmpl +++ b/templates/group.tmpl @@ -44,6 +44,17 @@ <input id="repo-desc-input" name="repo_desc" type="text" /> </td> </tr> + <tr> + <th scope="row">Contrib</th> + <td class="tdinput"> + <select id="repo-contrib-input" name="repo_contrib"> + <option value="public">Public</option> + <option value="registered_user">Registered user</option> + <option value="ssh_pubkey">SSH public key</option> + <option value="closed">Closed</option> + </select> + </td> + </tr> </tbody> <tfoot> <tr> @@ -99,6 +99,21 @@ func redirect_without_slash(w http.ResponseWriter, r *http.Request) bool { return false } +func redirect_unconditionally(w http.ResponseWriter, r *http.Request) { + request_uri := r.RequestURI + + path_end := strings.IndexAny(request_uri, "?#") + var path, rest string + if path_end == -1 { + path = request_uri + } else { + path = request_uri[:path_end] + rest = request_uri[path_end:] + } + + http.Redirect(w, r, path+rest, http.StatusSeeOther) +} + func path_escape_cat_segments(segments []string) string { for i, segment := range segments { segments[i] = url.PathEscape(segment) |