0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
|
// Copyright (C) 2023, Jakob Wakeling
// All rights reserved.
package goit
import (
"database/sql"
"errors"
"log"
"os"
"path/filepath"
"strings"
"github.com/Jamozed/Goit/src/util"
"github.com/go-git/go-git/v5"
gitconfig "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
)
type Repo struct {
Id int64 `json:"id"`
OwnerId int64 `json:"owner_id"`
Name string `json:"name"`
Description string `json:"description"`
DefaultBranch string `json:"default_branch"`
Upstream string `json:"upstream"`
Visibility Visibility `json:"visibility"`
IsMirror bool `json:"is_mirror"`
}
type Visibility int32
const (
Public Visibility = 0
Private Visibility = 1
Limited Visibility = 2
)
func VisibilityFromString(s string) Visibility {
switch strings.ToLower(s) {
case "public":
return Public
case "private":
return Private
case "limited":
return Limited
default:
return -1
}
}
func (v Visibility) String() string {
return [...]string{"public", "private", "limited"}[v]
}
func GetRepos() ([]Repo, error) {
repos := []Repo{}
rows, err := db.Query(
"SELECT id, owner_id, name, description, default_branch, upstream, visibility, is_mirror FROM repos",
)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
r := Repo{}
if err := rows.Scan(
&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.Visibility, &r.IsMirror,
); err != nil {
return nil, err
}
repos = append(repos, r)
}
if rows.Err() != nil {
return nil, err
}
return repos, nil
}
func GetRepo(rid int64) (*Repo, error) {
r := &Repo{}
if err := db.QueryRow(
`SELECT id, owner_id, name, description, default_branch, upstream, visibility, is_mirror FROM repos
WHERE id = ?`, rid,
).Scan(
&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.Visibility, &r.IsMirror,
); err != nil {
if !errors.Is(err, sql.ErrNoRows) {
return nil, err
}
return nil, nil
} else {
return r, nil
}
}
func GetRepoByName(name string) (*Repo, error) {
r := &Repo{}
if err := db.QueryRow(
`SELECT id, owner_id, name, description, default_branch, upstream, visibility, is_mirror FROM repos
WHERE name = ?`, name,
).Scan(
&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.Visibility, &r.IsMirror,
); err != nil {
if !errors.Is(err, sql.ErrNoRows) {
return nil, err
}
return nil, nil
}
return r, nil
}
func CreateRepo(repo Repo) (int64, error) {
tx, err := db.Begin()
if err != nil {
return -1, err
}
defer tx.Rollback()
res, err := tx.Exec(
`INSERT INTO repos (owner_id, name, name_lower, description, default_branch, upstream, visibility, is_mirror)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, repo.OwnerId, repo.Name, strings.ToLower(repo.Name), repo.Description,
repo.DefaultBranch, repo.Upstream, repo.Visibility, repo.IsMirror,
)
if err != nil {
return -1, err
}
r, err := git.PlainInit(RepoPath(repo.Name, true), true)
if err != nil {
return -1, err
}
ref := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.NewBranchReferenceName(repo.DefaultBranch))
if err := r.Storer.SetReference(ref); err != nil {
os.RemoveAll(RepoPath(repo.Name, true))
return -1, err
}
if repo.Upstream != "" {
if _, err := r.CreateRemote(&gitconfig.RemoteConfig{
Name: "origin",
URLs: []string{repo.Upstream},
Mirror: util.If(repo.IsMirror, true, false),
Fetch: []gitconfig.RefSpec{gitconfig.RefSpec("+refs/heads/*:refs/heads/*")},
}); err != nil {
os.RemoveAll(RepoPath(repo.Name, true))
return -1, err
}
}
if err := tx.Commit(); err != nil {
os.RemoveAll(RepoPath(repo.Name, true))
return -1, err
}
rid, _ := res.LastInsertId()
return rid, nil
}
func DelRepo(rid int64) error {
repo, err := GetRepo(rid)
if err != nil {
return err
}
if err := os.RemoveAll(RepoPath(repo.Name, true)); err != nil {
return err
}
if _, err := db.Exec("DELETE FROM repos WHERE id = ?", rid); err != nil {
return err
}
Cron.RemoveFor(rid)
Cron.Update()
return nil
}
func RepoExists(name string) (bool, error) {
if err := db.QueryRow(
"SELECT name FROM repos WHERE name_lower = ?", strings.ToLower(name),
).Scan(&name); err != nil {
if !errors.Is(err, sql.ErrNoRows) {
return false, err
} else {
return false, nil
}
} else {
return true, nil
}
}
func UpdateRepo(rid int64, repo Repo) error {
old, err := GetRepo(rid)
if err != nil {
return err
}
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if _, err := tx.Exec(
`UPDATE repos SET name = ?, name_lower = ?, description = ?, default_branch = ?, upstream = ?, visibility = ?,
is_mirror = ? WHERE id = ?`, repo.Name, strings.ToLower(repo.Name), repo.Description, repo.DefaultBranch,
repo.Upstream, repo.Visibility, repo.IsMirror, rid,
); err != nil {
return err
}
if repo.Name != old.Name {
if err := os.MkdirAll(filepath.Dir(RepoPath(repo.Name, true)), 0o777); err != nil {
return err
}
if err := os.Rename(RepoPath(old.Name, true), RepoPath(repo.Name, true)); err != nil {
return err
}
}
var r *git.Repository
if repo.DefaultBranch != old.DefaultBranch {
// if r == nil {
r, err = git.PlainOpen(RepoPath(repo.Name, true))
if err != nil {
return err
}
// }
ref := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.NewBranchReferenceName(repo.DefaultBranch))
if err := r.Storer.SetReference(ref); err != nil {
return err
}
}
/* If the upstream URL has been removed, remove the remote */
if repo.Upstream == "" && old.Upstream != "" {
if r == nil {
r, err = git.PlainOpen(RepoPath(repo.Name, true))
if err != nil {
return err
}
}
if err := r.DeleteRemote("origin"); err != nil {
return err
}
}
/* If the upstream URL has been added or changed, update the remote */
if repo.Upstream != "" && repo.Upstream != old.Upstream {
if r == nil {
r, err = git.PlainOpen(RepoPath(repo.Name, true))
if err != nil {
return err
}
}
if err := r.DeleteRemote("origin"); err != nil && !errors.Is(err, git.ErrRemoteNotFound) {
return err
}
if _, err := r.CreateRemote(&gitconfig.RemoteConfig{
Name: "origin",
URLs: []string{repo.Upstream},
Mirror: util.If(repo.IsMirror, true, false),
Fetch: []gitconfig.RefSpec{gitconfig.RefSpec("+refs/heads/*:refs/heads/*")},
}); err != nil {
log.Println("[repo/update]", err.Error())
}
}
if err := tx.Commit(); err != nil {
os.Rename(RepoPath(repo.Name, true), RepoPath(old.Name, true))
log.Println("[repo/update]", "error while editing, check repo \""+old.Name+"\"/\""+repo.Name+"\"")
return err
}
return nil
}
func ChownRepo(rid int64, uid int64) error {
if _, err := db.Exec("UPDATE repos SET owner_id = ? WHERE id = ?", uid, rid); err != nil {
return err
}
return nil
}
func Pull(rid int64) error {
repo, err := GetRepo(rid)
if err != nil {
return err
}
r, err := git.PlainOpen(RepoPath(repo.Name, true))
if err != nil {
return err
}
if err := r.Fetch(&git.FetchOptions{}); err != nil {
return err
}
return nil
}
func IsVisible(repo *Repo, auth bool, user *User) bool {
if repo.Visibility == Public || (repo.Visibility == Limited && auth) || (auth && user.Id == repo.OwnerId) {
return true
}
return false
}
|