Lindenii Project Forge
Login

server

Lindenii Forge’s main backend daemon
Commit info
ID
87b1ab519c77816d1c68a691e70137bd6f1c234b
Author
Runxi Yu <me@runxiyu.org>
Author date
Wed, 19 Mar 2025 12:06:54 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Wed, 19 Mar 2025 12:06:54 +0800
Actions
Remove underscores from Go code, pt 5
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileContributor: Runxi Yu <https://runxiyu.org>

package main

import (
	"bufio"
	"context"
	"errors"
	"io"
	"net/http"
	"net/url"
	"strings"

	"github.com/jackc/pgx/v5"
)

func fedauth(ctx context.Context, userID int, service, remote_username, pubkey string) (bool, error) {
func fedauth(ctx context.Context, userID int, service, remoteUsername, pubkey string) (bool, error) {
	var err error
	var resp *http.Response
	matched := false
	username_escaped := url.PathEscape(remote_username)
	usernameEscaped := url.PathEscape(remoteUsername)
	switch service {
	case "sr.ht":
		resp, err = http.Get("https://meta.sr.ht/~" + username_escaped + ".keys")
		resp, err = http.Get("https://meta.sr.ht/~" + usernameEscaped + ".keys")
	case "github":
		resp, err = http.Get("https://github.com/" + username_escaped + ".keys")
		resp, err = http.Get("https://github.com/" + usernameEscaped + ".keys")
	case "codeberg":
		resp, err = http.Get("https://codeberg.org/" + username_escaped + ".keys")
		resp, err = http.Get("https://codeberg.org/" + usernameEscaped + ".keys")
	case "tangled":
		resp, err = http.Get("https://tangled.sh/keys/" + username_escaped)
		resp, err = http.Get("https://tangled.sh/keys/" + usernameEscaped)
		// TODO: Don't rely on one webview
	default:
		return false, errors.New("unknown federated service")
	}

	if err != nil {
		return false, err
	}

	defer func() {
		_ = resp.Body.Close()
	}()
	buf := bufio.NewReader(resp.Body)

	for {
		line, err := buf.ReadString('\n')
		if errors.Is(err, io.EOF) {
			break
		} else if err != nil {
			return false, err
		}

		lineSplit := strings.Split(line, " ")
		if len(lineSplit) < 2 {
			continue
		}
		line = strings.Join(lineSplit[:2], " ")

		if line == pubkey {
			matched = true
			break
		}
	}

	if !matched {
		return false, nil
	}

	var tx pgx.Tx
	if tx, err = database.Begin(ctx); err != nil {
		return false, err
	}
	defer func() {
		_ = tx.Rollback(ctx)
	}()
	if _, err = tx.Exec(ctx, `UPDATE users SET type = 'federated' WHERE id = $1 AND type = 'pubkey_only'`, userID); err != nil {
		return false, err
	}
	if _, err = tx.Exec(ctx, `INSERT INTO federated_identities (user_id, service, remote_username) VALUES ($1, $2, $3)`, userID, service, remote_username); err != nil {
	if _, err = tx.Exec(ctx, `INSERT INTO federated_identities (user_id, service, remote_username) VALUES ($1, $2, $3)`, userID, service, remoteUsername); err != nil {
		return false, err
	}
	if err = tx.Commit(ctx); err != nil {
		return false, err
	}

	return true, nil
}
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileContributor: Runxi Yu <https://runxiyu.org>

package main

import (
	"io"
	"io/fs"
	"os"
	"path/filepath"
)

// deployHooks deploys the git hooks client to the filesystem.
// The git hooks client is expected to be embedded in resources_fs and must be
// pre-compiled during the build process; see the Makefile.
func deployHooks() (err error) {
	err = func() (err error) {
		var srcFD fs.File
		var dstFD *os.File

		if srcFD, err = resourcesFS.Open("hookc/hookc"); err != nil {
			return err
		}
		defer srcFD.Close()

		if dstFD, err = os.OpenFile(filepath.Join(config.Hooks.Execs, "hookc"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755); err != nil {
			return err
		}
		defer dstFD.Close()

		if _, err = io.Copy(dstFD, srcFD); err != nil {
			return err
		}

		return nil
	}()
	if err != nil {
		return err
	}

	// Go's embed filesystems do not store permissions; but in any case,
	// they would need to be 0o755:
	if err = os.Chmod(filepath.Join(config.Hooks.Execs, "hookc"), 0o755); err != nil {
		return err
	}

	for _, hook_name := range []string{
	for _, hookName := range []string{
		"pre-receive",
	} {
		if err = os.Symlink(filepath.Join(config.Hooks.Execs, "hookc"), filepath.Join(config.Hooks.Execs, hook_name)); err != nil {
		if err = os.Symlink(filepath.Join(config.Hooks.Execs, "hookc"), filepath.Join(config.Hooks.Execs, hookName)); err != nil {
			return err
		}
	}

	return nil
}
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileContributor: Runxi Yu <https://runxiyu.org>

package main

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/filemode"
	"github.com/go-git/go-git/v5/plumbing/format/diff"
	"github.com/go-git/go-git/v5/plumbing/object"
	"go.lindenii.runxiyu.org/lindenii-common/misc"
)

// The file patch type from go-git isn't really usable in HTML templates
// either.
type usableFilePatch struct {
	From   diff.File
	To     diff.File
	Chunks []usableChunk
}

type usableChunk struct {
	Operation diff.Operation
	Content   string
}

func httpHandleRepoCommit(w http.ResponseWriter, r *http.Request, params map[string]any) {
	var repo *git.Repository
	var commitIDStrSpec, commitIDStrSpecNoSuffix string
	var commitID plumbing.Hash
	var parentCommitHash plumbing.Hash
	var commitObj *object.Commit
	var commitIDStr string
	var err error
	var patch *object.Patch

	repo, commitIDStrSpec = params["repo"].(*git.Repository), params["commit_id"].(string)

	commitIDStrSpecNoSuffix = strings.TrimSuffix(commitIDStrSpec, ".patch")
	commitID = plumbing.NewHash(commitIDStrSpecNoSuffix)
	if commitObj, err = repo.CommitObject(commitID); err != nil {
		http.Error(w, "Error getting commit object: "+err.Error(), http.StatusInternalServerError)
		return
	}
	if commitIDStrSpecNoSuffix != commitIDStrSpec {
		var patchStr string
		if patchStr, err = fmtCommitPatch(commitObj); err != nil {
			http.Error(w, "Error formatting patch: "+err.Error(), http.StatusInternalServerError)
			return
		}
		fmt.Fprintln(w, patchStr)
		return
	}
	commitIDStr = commitObj.Hash.String()

	if commitIDStr != commitIDStrSpec {
		http.Redirect(w, r, commitIDStr, http.StatusSeeOther)
		return
	}

	params["commit_object"] = commitObj
	params["commit_id"] = commitIDStr

	parentCommitHash, patch, err = fmtCommitAsPatch(commitObj)
	if err != nil {
		http.Error(w, "Error getting patch from commit: "+err.Error(), http.StatusInternalServerError)
		return
	}
	params["parent_commit_hash"] = parentCommitHash.String()
	params["parent_commitHash"] = parentCommitHash.String()
	params["patch"] = patch

	params["file_patches"] = makeUsableFilePatches(patch)

	renderTemplate(w, "repo_commit", params)
}

type fakeDiffFile struct {
	hash plumbing.Hash
	mode filemode.FileMode
	path string
}

func (f fakeDiffFile) Hash() plumbing.Hash {
	return f.hash
}

func (f fakeDiffFile) Mode() filemode.FileMode {
	return f.mode
}

func (f fakeDiffFile) Path() string {
	return f.path
}

var nullFakeDiffFile = fakeDiffFile{
	hash: plumbing.NewHash("0000000000000000000000000000000000000000"),
	mode: misc.First_or_panic(filemode.New("100644")),
	path: "",
}

func makeUsableFilePatches(patch diff.Patch) (usableFilePatches []usableFilePatch) {
	// TODO: Remove unnecessary context
	// TODO: Prepend "+"/"-"/" " instead of solely distinguishing based on color

	for _, filePatch := range patch.FilePatches() {
		var from, to diff.File
		var ufp usableFilePatch
		chunks := []usableChunk{}

		from, to = filePatch.Files()
		if from == nil {
			from = nullFakeDiffFile
		}
		if to == nil {
			to = nullFakeDiffFile
		}
		for _, chunk := range filePatch.Chunks() {
			var content string

			content = chunk.Content()
			if len(content) > 0 && content[0] == '\n' {
				content = "\n" + content
			} // Horrible hack to fix how browsers newlines that immediately proceed <pre>
			chunks = append(chunks, usableChunk{
				Operation: chunk.Type(),
				Content:   content,
			})
		}
		ufp = usableFilePatch{
			Chunks: chunks,
			From:   from,
			To:     to,
		}
		usableFilePatches = append(usableFilePatches, ufp)
	}
	return
}
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileContributor: Runxi Yu <https://runxiyu.org>

package main

import (
	"net/http"
	"strings"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/go-git/go-git/v5/plumbing/storer"
)

func httpHandleRepoIndex(w http.ResponseWriter, r *http.Request, params map[string]any) {
	var repo *git.Repository
	var repo_name string
	var group_path []string
	var ref_hash plumbing.Hash
	var refHash plumbing.Hash
	var err error
	var recent_commits []*object.Commit
	var commit_object *object.Commit
	var tree *object.Tree
	var notes []string
	var branches []string
	var branches_ storer.ReferenceIter

	repo, repo_name, group_path = params["repo"].(*git.Repository), params["repo_name"].(string), params["group_path"].([]string)

	if strings.Contains(repo_name, "\n") || slice_contains_newline(group_path) {
		notes = append(notes, "Path contains newlines; HTTP Git access impossible")
	}

	ref_hash, err = getRefHash(repo, params["ref_type"].(string), params["ref_name"].(string))
	refHash, err = getRefHash(repo, params["ref_type"].(string), params["ref_name"].(string))
	if err != nil {
		goto no_ref
	}

	branches_, err = repo.Branches()
	if err != nil {
	}
	err = branches_.ForEach(func(branch *plumbing.Reference) error {
		branches = append(branches, branch.Name().Short())
		return nil
	})
	if err != nil {
	}
	params["branches"] = branches

	if recent_commits, err = getRecentCommits(repo, ref_hash, 3); err != nil {
	if recent_commits, err = getRecentCommits(repo, refHash, 3); err != nil {
		goto no_ref
	}
	params["commits"] = recent_commits

	if commit_object, err = repo.CommitObject(ref_hash); err != nil {
	if commit_object, err = repo.CommitObject(refHash); err != nil {
		goto no_ref
	}

	if tree, err = commit_object.Tree(); err != nil {
		goto no_ref
	}

	params["files"] = makeDisplayTree(tree)
	params["readme_filename"], params["readme"] = renderReadmeAtTree(tree)

no_ref:

	params["http_clone_url"] = genHTTPRemoteURL(group_path, repo_name)
	params["ssh_clone_url"] = genSSHRemoteURL(group_path, repo_name)
	params["notes"] = notes

	renderTemplate(w, "repo_index", params)
}
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileContributor: Runxi Yu <https://runxiyu.org>

package main

import (
	"net/http"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
)

// TODO: I probably shouldn't include *all* commits here...
func httpHandleRepoLog(w http.ResponseWriter, r *http.Request, params map[string]any) {
	var repo *git.Repository
	var ref_hash plumbing.Hash
	var refHash plumbing.Hash
	var err error
	var commits []*object.Commit

	repo = params["repo"].(*git.Repository)

	if ref_hash, err = getRefHash(repo, params["ref_type"].(string), params["ref_name"].(string)); err != nil {
	if refHash, err = getRefHash(repo, params["ref_type"].(string), params["ref_name"].(string)); err != nil {
		http.Error(w, "Error getting ref hash: "+err.Error(), http.StatusInternalServerError)
		return
	}

	if commits, err = getRecentCommits(repo, ref_hash, -1); err != nil {
	if commits, err = getRecentCommits(repo, refHash, -1); err != nil {
		http.Error(w, "Error getting recent commits: "+err.Error(), http.StatusInternalServerError)
		return
	}
	params["commits"] = commits

	renderTemplate(w, "repo_log", params)
}
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileContributor: Runxi Yu <https://runxiyu.org>

package main

import (
	"fmt"
	"net/http"
	"path"
	"strings"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
)

func httpHandleRepoRaw(w http.ResponseWriter, r *http.Request, params map[string]any) {
	var raw_path_spec, path_spec string
	var repo *git.Repository
	var ref_hash plumbing.Hash
	var refHash plumbing.Hash
	var commit_object *object.Commit
	var tree *object.Tree
	var err error

	raw_path_spec = params["rest"].(string)
	repo, path_spec = params["repo"].(*git.Repository), strings.TrimSuffix(raw_path_spec, "/")
	params["path_spec"] = path_spec

	if ref_hash, err = getRefHash(repo, params["ref_type"].(string), params["ref_name"].(string)); err != nil {
	if refHash, err = getRefHash(repo, params["ref_type"].(string), params["ref_name"].(string)); err != nil {
		http.Error(w, "Error getting ref hash: "+err.Error(), http.StatusInternalServerError)
		return
	}

	if commit_object, err = repo.CommitObject(ref_hash); err != nil {
	if commit_object, err = repo.CommitObject(refHash); err != nil {
		http.Error(w, "Error getting commit object: "+err.Error(), http.StatusInternalServerError)
		return
	}
	if tree, err = commit_object.Tree(); err != nil {
		http.Error(w, "Error getting file tree: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var target *object.Tree
	if path_spec == "" {
		target = tree
	} else {
		if target, err = tree.Tree(path_spec); err != nil {
			var file *object.File
			var file_contents string
			if file, err = tree.File(path_spec); err != nil {
				http.Error(w, "Error retrieving path: "+err.Error(), http.StatusInternalServerError)
				return
			}
			if len(raw_path_spec) != 0 && raw_path_spec[len(raw_path_spec)-1] == '/' {
				http.Redirect(w, r, "../"+path_spec, http.StatusSeeOther)
				return
			}
			if file_contents, err = file.Contents(); err != nil {
				http.Error(w, "Error reading file: "+err.Error(), http.StatusInternalServerError)
				return
			}
			fmt.Fprint(w, file_contents)
			return
		}
	}

	if len(raw_path_spec) != 0 && raw_path_spec[len(raw_path_spec)-1] != '/' {
		http.Redirect(w, r, path.Base(path_spec)+"/", http.StatusSeeOther)
		return
	}

	params["files"] = makeDisplayTree(target)

	renderTemplate(w, "repo_raw_dir", params)
}