Lindenii Project Forge
Commit info | |
---|---|
ID | ac956e5521b4ad1cce1f978cc1aef51e6aeb9480 |
Author | Runxi Yu<me@runxiyu.org> |
Author date | Thu, 13 Feb 2025 10:29:57 +0800 |
Committer | Runxi Yu<me@runxiyu.org> |
Committer date | Thu, 13 Feb 2025 10:31:31 +0800 |
Actions | Get patch |
index: Reformat the page
package main import ( "context" ) func query_list[T any](ctx context.Context, query string, args ...any) ([]T, error) { rows, err := database.Query(ctx, query, args...) if err != nil { return nil, err } defer rows.Close() var result []T for rows.Next() { var item T if err := rows.Scan(&item); err != nil { return nil, err } result = append(result, item) } if err := rows.Err(); err != nil { return nil, err } return result, nil }
package main import ( "net/http" ) func handle_group_repos(w http.ResponseWriter, r *http.Request, params map[string]any) { group_name := params["group_name"] names, err := query_list[string](r.Context(), "SELECT r.name FROM repos r JOIN groups g ON r.group_id = g.id WHERE g.name = $1;", group_name) if err != nil {
http.Error(w, "Error getting groups:: "+err.Error(), http.StatusInternalServerError) return
http.Error(w, "Error getting groups:: "+err.Error(), http.StatusInternalServerError) return
} params["repos"] = names err = templates.ExecuteTemplate(w, "group_repos", params) if err != nil { http.Error(w, "Error rendering template:: "+err.Error(), http.StatusInternalServerError) return } }
package main import ( "net/http" ) func handle_index(w http.ResponseWriter, r *http.Request, params map[string]any) {
rows, err := database.Query(r.Context(), "SELECT name FROM groups")
rows, err := database.Query(r.Context(), "SELECT name, COALESCE(description, '') FROM groups")
if err != nil {
http.Error(w, "Error querying groups: : "+err.Error(), http.StatusInternalServerError)
http.Error(w, "Error querying groups: "+err.Error(), http.StatusInternalServerError)
return } defer rows.Close()
groups := []string{}
groups := []struct { Name string Description string }{}
for rows.Next() {
var groupName string if err := rows.Scan(&groupName); err != nil { http.Error(w, "Error scanning group name: : "+err.Error(), http.StatusInternalServerError)
var groupName, groupDescription string if err := rows.Scan(&groupName, &groupDescription); err != nil { http.Error(w, "Error scanning group: "+err.Error(), http.StatusInternalServerError)
return }
groups = append(groups, groupName)
groups = append(groups, struct { Name string Description string }{groupName, groupDescription})
} if err := rows.Err(); err != nil {
http.Error(w, "Error iterating over rows: : "+err.Error(), http.StatusInternalServerError)
http.Error(w, "Error iterating over rows: "+err.Error(), http.StatusInternalServerError)
return } params["groups"] = groups err = templates.ExecuteTemplate(w, "index", params) if err != nil {
http.Error(w, "Error rendering template: : "+err.Error(), http.StatusInternalServerError)
http.Error(w, "Error rendering template: "+err.Error(), http.StatusInternalServerError)
return } }
CREATE TABLE groups ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL UNIQUE
name TEXT NOT NULL UNIQUE, description TEXT
); CREATE TABLE repos ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE RESTRICT, -- I mean, should be CASCADE but deleting Git repos on disk also needs to be considered name TEXT NOT NULL, UNIQUE(group_id, name), description TEXT, filesystem_path TEXT ); CREATE TABLE ticket_trackers ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE RESTRICT, name TEXT NOT NULL, UNIQUE(group_id, name), description TEXT ); CREATE TABLE tickets ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, tracker_id INTEGER NOT NULL REFERENCES ticket_trackers(id) ON DELETE CASCADE, title TEXT NOT NULL, description TEXT ); CREATE TABLE mailing_lists ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE RESTRICT, name TEXT NOT NULL, UNIQUE(group_id, name), description TEXT ); CREATE TABLE emails ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, list_id INTEGER NOT NULL REFERENCES mailing_lists(id) ON DELETE CASCADE, title TEXT NOT NULL, sender TEXT NOT NULL, date TIMESTAMP NOT NULL, content BYTEA NOT NULL ); CREATE TABLE users ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, username TEXT NOT NULL UNIQUE, password TEXT NOT NULL ); CREATE TABLE sessions ( user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, session_id TEXT NOT NULL, PRIMARY KEY (user_id, session_id) ); CREATE TABLE merge_requests ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, repo_id INTEGER NOT NULL REFERENCES repos(id) ON DELETE CASCADE, creator INTEGER REFERENCES users(id) ON DELETE SET NULL, source_ref TEXT NOT NULL, destination_branch TEXT NOT NULL, status TEXT NOT NULL CHECK (status IN ('open', 'merged', 'closed')), created_at TIMESTAMP NOT NULL, mailing_list_id INT UNIQUE REFERENCES mailing_lists(id) ON DELETE CASCADE ); CREATE TABLE ssh_public_keys ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, content TEXT NOT NULL, UNIQUE (user_id, content) );
{{- define "index" -}} <!DOCTYPE html> <html lang="en"> <head> {{ template "head_common" . }} <title>Index – Lindenii Forge</title> </head> <body class="index"> {{ template "header" . }} <div class="padding-wrapper">
<h1>Lindenii Forge</h1> <h2> Groups </h2> <ul> {{- range .groups }} <li> <a href="{{ . }}/:/repos/">{{ . }}</a> </li> {{- end }} </ul> <h2> Info </h2>
<table class="wide">
<thead> <tr> <th colspan="2" class="title-row"> Groups </th> </tr> </thead> <tbody> {{- range .groups }} <tr> <td> <a href="{{ .Name }}/:/repos/">{{ .Name }}</a> </td> <td> {{ .Description }} </td> </tr> {{- end }} </tbody> </table> </div> <div class="padding-wrapper"> <table class="wide"> <thead> <tr> <th colspan="2" class="title-row"> Info </th> </tr> </thead>
<tbody> <tr> <th scope="row">SSH Public Key</th> <td><code>{{ .global.server_public_key_string }}</code></td> </tr> <tr> <th scope="row">SSH Fingerprint</th> <td><code>{{ .global.server_public_key_fingerprint }}</code></td> </tr> </tbody> </table> </div> <footer> {{ template "footer" . }} </footer> </body> </html> {{- end -}}