Hi… I am well aware that this diff view is very suboptimal. It will be fixed when the refactored server comes along!
Start doing the group index page...
package handlers import (
"log"
"net/http"
"strings"
"go.lindenii.runxiyu.org/forge/forged/internal/database/queries"
"go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/templates"
wtypes "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/types"
)
type GroupHTTP struct {
r templates.Renderer
}
func NewGroupHTTP(r templates.Renderer) *GroupHTTP {
return &GroupHTTP{
r: r,
}
}
func (h *GroupHTTP) Index(w http.ResponseWriter, r *http.Request, _ wtypes.Vars) {
base := wtypes.Base(r)
_ = h.r.Render(w, "group/index.html", struct {
GroupPath string
p, err := base.Queries.GetGroupIDDescByPath(r.Context(), base.URLSegments)
if err != nil {
log.Println("failed to get group ID by path", "error", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
subgroups, err := base.Queries.GetSubgroups(r.Context(), &p.ID)
if err != nil {
log.Println("failed to get subgroups", "error", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
// TODO
}
repos, err := base.Queries.GetReposInGroup(r.Context(), p.ID)
if err != nil {
log.Println("failed to get repos in group", "error", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
// TODO
}
err = h.r.Render(w, "group", struct {
BaseData *wtypes.BaseData
Subgroups []queries.GetSubgroupsRow
Repos []queries.GetReposInGroupRow
Description string
}{
GroupPath: "/" + strings.Join(base.GroupPath, "/") + "/",
BaseData: base, Subgroups: subgroups, Repos: repos, Description: p.Description,
})
if err != nil {
log.Println("failed to render index page", "error", err)
}
}
-- name: GetRootGroups :many SELECT name, COALESCE(description, '') FROM groups WHERE parent_group IS NULL; -- name: GetGroupIDDescByPath :one WITH RECURSIVE group_path_cte AS ( SELECT id, parent_group, name, 1 AS depth FROM groups WHERE name = ($1::text[])[1] AND parent_group IS NULL UNION ALL SELECT g.id, g.parent_group, g.name, group_path_cte.depth + 1 FROM groups g JOIN group_path_cte ON g.parent_group = group_path_cte.id WHERE g.name = ($1::text[])[group_path_cte.depth + 1] AND group_path_cte.depth + 1 <= cardinality($1::text[]) ) SELECT c.id, COALESCE(g.description, '') FROM group_path_cte c JOIN groups g ON g.id = c.id WHERE c.depth = cardinality($1::text[]);
-- name: GetReposInGroup :many SELECT name, COALESCE(description, '') FROM repos WHERE group_id = $1; -- name: GetSubgroups :many SELECT name, COALESCE(description, '') FROM groups WHERE parent_group = $1;
{{/*
SPDX-License-Identifier: AGPL-3.0-only
SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
*/}}
{{- define "group_view" -}}
{{- if .subgroups -}}
{{- if .Subgroups -}}
<table class="wide"> <thead> <tr> <th colspan="2" class="title-row">Subgroups</th> </tr> <tr> <th scope="col">Name</th> <th scope="col">Description</th> </tr> </thead> <tbody>
{{- range .subgroups -}}
{{- range .Subgroups -}}
<tr>
<td>
<a href="{{- .Name | path_escape -}}/">{{- .Name -}}</a>
</td>
<td>
{{- .Description -}}
</td>
</tr>
{{- end -}}
</tbody>
</table>
{{- end -}}
{{- if .repos -}}
{{- if .Repos -}}
<table class="wide"> <thead> <tr> <th colspan="2" class="title-row">Repos</th> <tr> <th scope="col">Name</th> <th scope="col">Description</th> </tr> </tr> </thead> <tbody>
{{- range .repos -}}
{{- range .Repos -}}
<tr>
<td>
<a href="-/repos/{{- .Name | path_escape -}}/">{{- .Name -}}</a>
</td>
<td>
{{- .Description -}}
</td>
</tr>
{{- end -}}
</tbody>
</table>
{{- end -}}
{{- end -}}
{{/*
SPDX-License-Identifier: AGPL-3.0-only
SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
*/}}
{{- define "group" -}}
{{- $group_path := .group_path -}}
{{- $group_path := .BaseData.GroupPath -}}
<!DOCTYPE html>
<html lang="en">
<head>
{{- template "head_common" . -}}
<title>{{- range $i, $s := .group_path -}}{{- $s -}}{{- if ne $i (len $group_path) -}}/{{- end -}}{{- end }} – {{ .global.forge_title -}}</title>
<title>{{- range $i, $s := $group_path -}}{{- $s -}}{{- if ne $i (len $group_path) -}}/{{- end -}}{{- end }} – {{ .BaseData.Global.ForgeTitle -}}</title>
</head>
<body class="group">
{{- template "header" . -}}
<main>
<div class="padding-wrapper">
{{- if .description -}}
<p>{{- .description -}}</p>
{{- if .Description -}}
<p>{{- .Description -}}</p>
{{- end -}}
{{- template "group_view" . -}}
</div>
{{- if .direct_access -}}
<div class="padding-wrapper">
<form method="POST" enctype="application/x-www-form-urlencoded">
<table>
<thead>
<tr>
<th class="title-row" colspan="2">
Create repo
</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Name</th>
<td class="tdinput">
<input id="repo-name-input" name="repo_name" type="text" />
</td>
</tr>
<tr>
<th scope="row">Description</th>
<td class="tdinput">
<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="ssh_pubkey">SSH public key</option>
<option value="federated">Federated service</option>
<option value="registered_user">Registered user</option>
<option value="closed">Closed</option>
</select>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td class="th-like" colspan="2">
<div class="flex-justify">
<div class="left">
</div>
<div class="right">
<input class="btn-primary" type="submit" value="Create" />
</div>
</div>
</td>
</tr>
</tfoot>
</table>
</form>
</div>
{{- end -}}
</main>
<footer>
{{- template "footer" . -}}
</footer>
</body>
</html>
{{- end -}}