This repository has been archived on 2024-03-28. You can view files and clone it, but cannot push or open issues or pull requests.
atsebay.t/repositories.go

814 lines
24 KiB
Go

package main
import (
"crypto/rand"
"encoding/base64"
"flag"
"fmt"
"log"
"net/http"
"net/url"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/drone/drone-go/drone"
"github.com/gin-gonic/gin"
"golang.org/x/oauth2"
)
var (
droneToken = ""
droneConfig *http.Client
droneEndpoint string
testsCallbackToken string
)
func init() {
flag.StringVar(&droneToken, "drone-token", droneToken, "Token for Drone Oauth")
flag.StringVar(&droneEndpoint, "drone-endpoint", droneEndpoint, "Drone Endpoint")
flag.StringVar(&testsCallbackToken, "tests-callback-token", testsCallbackToken, "Token of the callback token")
}
func initializeDroneOauth() {
if droneToken != "" {
config := new(oauth2.Config)
droneConfig = config.Client(
oauth2.NoContext,
&oauth2.Token{
AccessToken: droneToken,
},
)
}
}
type RepositoryAdminPull struct {
Tag *string `json:"tag"`
OptionalSignature bool `json:"sig_optional"`
}
func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
router.GET("/repositories", func(c *gin.Context) {
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = c.MustGet("LoggedUser").(*User)
}
repositories, err := u.GetRepositories()
if err != nil {
log.Println("Unable to GetRepositories:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve your repositories. Please try again in a few moment."})
return
}
if work, ok := c.Get("work"); ok {
var res []*Repository
for _, r := range repositories {
if r.IdWork == work.(*Work).Id {
// Is the URL used elsewhere?
repos, _ := getRepositoriesByURI(r.URI)
if len(repos) > 1 {
r.AlreadyUsed = true
}
res = append(res, r)
}
}
c.JSON(http.StatusOK, res)
} else {
c.JSON(http.StatusOK, repositories)
}
})
router.POST("/repositories", func(c *gin.Context) {
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = c.MustGet("LoggedUser").(*User)
}
var repository Repository
if err := c.ShouldBindJSON(&repository); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
uri, err := url.Parse(repository.URI)
if err != nil {
tmp := strings.Split(repository.URI, ":")
if len(tmp) == 2 {
uri, err = url.Parse(fmt.Sprintf("ssh://%s/%s", tmp[0], tmp[1]))
} else if len(tmp) == 3 {
uri, err = url.Parse(fmt.Sprintf("%s://%s/%s", tmp[0], tmp[1], tmp[2]))
}
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("invalid repository URL: %s", err.Error())})
return
}
}
if uri.Scheme != "ssh" && uri.Scheme != "git+ssh" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unrecognized URL scheme. You need to provide a SSH repository URL."})
return
}
if strings.Contains(uri.Host, "epita.fr") {
if !strings.HasPrefix(uri.Path, fmt.Sprintf("/%s/", u.Login)) {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "repository URL forbidden"})
return
}
}
var w *Work
if work, ok := c.Get("work"); ok {
w = work.(*Work)
} else if repository.IdWork > 0 {
var err error
w, err = getWork(repository.IdWork)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Unable to find the given work identifier."})
return
}
} else {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unable to find the given work identifier."})
return
}
k, err := u.NewRepository(w, repository.URI)
if err != nil {
log.Println("Unable to NewRepository:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to register your public repository. Please try again in a few moment."})
return
}
c.JSON(http.StatusOK, k)
})
repositoriesRoutes := router.Group("/repositories/:rid")
repositoriesRoutes.Use(repositoryHandler)
repositoriesRoutes.GET("", func(c *gin.Context) {
repo := c.MustGet("repository").(*Repository)
// Is the URL used elsewhere?
repos, _ := getRepositoriesByURI(repo.URI)
if len(repos) > 1 {
repo.AlreadyUsed = true
}
c.JSON(http.StatusOK, repo)
})
repositoriesRoutes.PUT("", func(c *gin.Context) {
current := c.MustGet("repository").(*Repository)
var new Repository
if err := c.ShouldBindJSON(&new); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
new.Id = current.Id
u := c.MustGet("LoggedUser").(*User)
if new.IdUser != current.IdUser && !u.IsAdmin {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Operation not allowed."})
return
}
if repository, err := new.Update(); err != nil {
log.Println("Unable to Update repository:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during repository updation: %s", err.Error())})
return
} else {
c.JSON(http.StatusOK, repository)
}
})
repositoriesRoutes.DELETE("", func(c *gin.Context) {
loggeduser := c.MustGet("LoggedUser").(*User)
repository := c.MustGet("repository").(*Repository)
work, err := getWork(repository.IdWork)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
return
}
now := time.Now()
if !loggeduser.IsAdmin && (!work.Shown || work.Corrected || work.StartAvailability.After(now) || work.EndAvailability.Before(now)) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "The submission is closed."})
return
}
if _, err := repository.Delete(); err != nil {
log.Println("Unable to Delete repository:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during repository deletion: %s", err.Error())})
return
} else {
c.JSON(http.StatusOK, nil)
}
})
repositoriesRoutes.POST("/trigger", func(c *gin.Context) {
loggeduser := c.MustGet("LoggedUser").(*User)
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = loggeduser
}
repo := c.MustGet("repository").(*Repository)
work, err := getWork(repo.IdWork)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
return
}
now := time.Now()
if repo.LastCheck != nil && !repo.LastCheck.Before(now.Add(-5*time.Minute)) && !loggeduser.IsAdmin {
c.AbortWithStatusJSON(http.StatusPaymentRequired, gin.H{"errmsg": "Please wait between two pulls."})
return
}
var rap RepositoryAdminPull
if loggeduser.IsAdmin {
c.ShouldBindJSON(&rap)
}
TriggerTagUpdate(c, work, repo, u, rap.Tag, rap.OptionalSignature)
})
repositoriesRoutes.GET("/state", func(c *gin.Context) {
repo := c.MustGet("repository").(*Repository)
tmp := strings.Split(repo.DroneRef, "/")
if len(tmp) < 3 {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "No build number. Please try pulling your work."})
return
}
nbuild, err := strconv.Atoi(tmp[2])
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Bad build number. Please retry pulling your work."})
return
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.Build(tmp[0], tmp[1], nbuild)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Unable to find the referenced extraction."})
return
}
c.JSON(http.StatusOK, result)
})
repositoriesRoutes.GET("/state-logs", func(c *gin.Context) {
repo := c.MustGet("repository").(*Repository)
tmp := strings.Split(repo.DroneRef, "/")
if len(tmp) < 3 {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "No build number. Please try pulling your work."})
return
}
nbuild, err := strconv.Atoi(tmp[2])
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Bad build number. Please retry pulling your work."})
return
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.Logs(tmp[0], tmp[1], nbuild, 1, 3)
if err != nil {
log.Printf("An error occurs when retrieving logs from Drone (%s/%s/%d/1/3): %s", tmp[0], tmp[1], nbuild, err.Error())
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Unable to retrieve logs."})
return
}
if len(result) > 7 {
c.JSON(http.StatusOK, result[7:])
} else {
c.JSON(http.StatusOK, result)
}
})
repositoriesRoutes.POST("/gradation", func(c *gin.Context) {
loggeduser := c.MustGet("LoggedUser").(*User)
if !loggeduser.IsAdmin {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Permission denied."})
return
}
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = loggeduser
}
repo := c.MustGet("repository").(*Repository)
work, err := getWork(repo.IdWork)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
return
}
TriggerTests(c, work, repo, u)
})
repositoriesRoutes.GET("/gradation_status", func(c *gin.Context) {
loggeduser := c.MustGet("LoggedUser").(*User)
if !loggeduser.IsAdmin {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Permission denied."})
return
}
repo := c.MustGet("repository").(*Repository)
slug := strings.Split(repo.TestsRef, "/")
if len(slug) < 3 {
return
}
buildn, err := strconv.ParseInt(slug[2], 10, 32)
if err != nil {
return
}
client := drone.NewClient(droneEndpoint, droneConfig)
build, err := client.Build(slug[0], slug[1], int(buildn))
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to communicate with Drone"})
return
}
c.JSON(http.StatusOK, build)
})
repositoriesRoutes.GET("/traces", func(c *gin.Context) {
repo := c.MustGet("repository").(*Repository)
c.Redirect(http.StatusFound, fmt.Sprintf("%s/%s", droneEndpoint, repo.TestsRef))
})
}
type GitLabWebhook struct {
EventName string `json:"event_name"`
ObjectKind string `json:"object_kind"`
Ref string
Repository GitLabRepository
}
func declareCallbacksRoutes(router *gin.RouterGroup) {
router.POST("/callbacks/trigger.json", func(c *gin.Context) {
// Check event type
if c.Request.Header.Get("X-Gitlab-Event") != "Tag Push Hook" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "This trigger is limited to Tag Push event. Please edit your trigger on GitLab."})
return
}
hook := GitLabWebhook{}
if err := c.ShouldBindJSON(&hook); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
// Check token
repos, err := getRepositoriesByURI(hook.Repository.URL)
if err != nil {
log.Println("Unable to getRepositoriesByURI:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve repositories."})
return
}
var repo *Repository
for _, r := range repos {
if len(r.Secret) == 0 || base64.StdEncoding.EncodeToString(r.Secret) == c.Request.Header.Get("X-Gitlab-Token") {
repo = r
break
}
}
if repo == nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "There are no repositories matching this URL and secret. Please check the Gitlab Secret and retry pushing your tag."})
return
}
work, err := getWork(repo.IdWork)
if err != nil {
log.Println("Unable to getWork:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
return
}
user, err := getUser(int(repo.IdUser))
if err != nil {
log.Println("Unable to getUser:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related user."})
return
}
tmp := strings.SplitN(hook.Ref, "/", 3)
if len(tmp) != 3 {
TriggerTagUpdate(c, work, repo, user, nil, false)
return
}
if !strings.HasPrefix(tmp[2], work.Tag) {
// Allow to use a secret for another tag
if len(repos) > 1 {
for _, r := range repos {
w, err := getWork(r.IdWork)
if err != nil {
log.Println("Unable to getWork:", err.Error())
continue
}
if strings.HasPrefix(tmp[2], w.Tag) {
repo = r
work = w
break
}
}
}
if !strings.HasPrefix(tmp[2], work.Tag) {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"errmsg": fmt.Sprintf("Ignore ref %q has it doesn't start with %s. Check submission instructions if this is not expected.", tmp[2], work.Tag)})
return
}
}
TriggerTagUpdate(c, work, repo, user, &tmp[2], false)
})
router.POST("/callbacks/tests.json", func(c *gin.Context) {
// Check auth token
if c.GetHeader("X-Authorization") != testsCallbackToken {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": "Authorization token is invalid"})
return
}
// Get form data
hook := TestsWebhook{}
if err := c.ShouldBindJSON(&hook); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
// Retrieve corresponding repository
repo, err := getRepository(hook.RepositoryId)
if err != nil {
log.Printf("Unable to getRepository(%d): %s", hook.RepositoryId, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve repository."})
return
}
err = hook.fetchRepoTests(repo)
if err != nil {
log.Printf("Unable to fetchRepoTests(%d): %s", hook.RepositoryId, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to fetch tests results."})
return
}
})
}
func repositoryHandler(c *gin.Context) {
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = c.MustGet("LoggedUser").(*User)
}
if rid, err := strconv.Atoi(string(c.Param("rid"))); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad repository identifier."})
return
} else if c.MustGet("LoggedUser").(*User).IsAdmin {
if repository, err := getRepository(rid); err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Repository not found."})
return
} else {
c.Set("repository", repository)
c.Next()
}
} else if repository, err := u.getRepository(rid); err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Repository not found."})
return
} else {
c.Set("repository", repository)
c.Next()
}
}
func TriggerTagUpdate(c *gin.Context, work *Work, repo *Repository, u *User, tag *string, sig_optional bool) {
loggeduser := c.MustGet("LoggedUser").(*User)
now := time.Now()
if (loggeduser == nil || !loggeduser.IsAdmin) && (!work.Shown || work.Corrected || work.StartAvailability.After(now) || work.EndAvailability.Add(time.Hour).Before(now)) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "The submission is closed."})
return
}
repo_tag := work.Tag
if tag != nil {
repo_tag = *tag
}
login := u.Login
groups := u.Groups
if u.Id != repo.IdUser {
user, _ := getUser(int(repo.IdUser))
if user != nil {
login = user.Login
groups = user.Groups
}
}
env := map[string]string{
"REPO_URL": repo.URI,
"REPO_TAG": repo_tag,
"LOGIN": login,
"GROUPS": groups,
"DEST": fmt.Sprintf("%d", work.Id),
}
if sig_optional {
env["TAG_SIG_OPTIONAL"] = "1"
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.BuildCreate("teach", "atsebay.t-worker", "", "master", env)
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to communication with the extraction service."})
return
}
repo.DroneRef = fmt.Sprintf("%s/%s/%d", "teach", "atsebay.t-worker", result.Number)
repo.LastCheck = &now
repo.Update()
repo.Secret = []byte{}
c.JSON(http.StatusOK, repo)
}
func TriggerTests(c *gin.Context, work *Work, repo *Repository, u *User) {
if work.GradationRepo == nil || len(*work.GradationRepo) == 0 {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "No tests defined for this work."})
return
}
slug := strings.SplitN(*work.GradationRepo, "/", 2)
if len(slug) != 2 {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Graduation repository is invalid."})
return
}
login := u.Login
groups := u.Groups
if u.Id != repo.IdUser {
user, _ := getUser(int(repo.IdUser))
if user != nil {
login = user.Login
groups = user.Groups
}
}
branch := "master"
if len(work.Tag) > 0 {
branch = work.Tag
}
if branch[len(branch)-1] == '-' {
branch += "grades"
}
s, err := s3NewSession()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong."})
return
}
req, _ := s3.New(s).GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(s3_bucket),
Key: aws.String(filepath.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.tar.xz", login))),
})
url, err := req.Presign(SharingTime * 20)
if err != nil {
log.Println("Unable to create presign URL:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong when creating the presigned URL."})
return
}
env := map[string]string{
"SUBMISSION_URL": repo.URI,
"REPO_ID": fmt.Sprintf("%d", repo.Id),
"META_URL": url,
"LOGIN": login,
"GROUPS": groups,
"DEST": fmt.Sprintf("%d", work.Id),
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.BuildCreate(slug[0], slug[1], "", branch, env)
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to communication with the gradation service."})
return
}
now := time.Now()
repo.TestsRef = fmt.Sprintf("%s/%d", *work.GradationRepo, result.Number)
repo.LastTests = &now
repo.Update()
repo.Secret = []byte{}
c.JSON(http.StatusOK, repo)
}
func (w *Work) stopTests() error {
repos, err := w.GetRepositories()
if err != nil {
return err
}
client := drone.NewClient(droneEndpoint, droneConfig)
for _, repo := range repos {
slug := strings.Split(repo.TestsRef, "/")
if len(slug) < 3 {
continue
}
buildn, err := strconv.ParseInt(slug[2], 10, 32)
if err != nil {
continue
}
build, err := client.Build(slug[0], slug[1], int(buildn))
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
continue
}
if build.Status == "pending" {
err := client.BuildCancel(slug[0], slug[1], int(buildn))
if err != nil {
log.Println("Unable to cancel the build:", err.Error())
continue
}
}
}
return nil
}
type Repository struct {
Id int64 `json:"id"`
IdUser int64 `json:"id_user"`
IdWork int64 `json:"id_work"`
URI string `json:"uri"`
Secret []byte `json:"secret,omitempty"`
LastCheck *time.Time `json:"last_check"`
DroneRef string `json:"drone_ref,omitempty"`
LastTests *time.Time `json:"last_tests"`
TestsRef string `json:"tests_ref,omitempty"`
AlreadyUsed bool `json:"already_used,omitempty"`
}
func (u *User) GetRepositories() (repositories []*Repository, err error) {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_user=?", u.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var repo Repository
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef, &repo.LastTests, &repo.TestsRef); err != nil {
return
}
repositories = append(repositories, &repo)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func (w *Work) GetRepositories() (repositories []*Repository, err error) {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_work=?", w.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var repo Repository
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef, &repo.LastTests, &repo.TestsRef); err != nil {
return
}
repositories = append(repositories, &repo)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func getRepositoriesByURI(uri string) (repositories []*Repository, err error) {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE uri=?", uri); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var repo Repository
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef, &repo.LastTests, &repo.TestsRef); err != nil {
return
}
repositories = append(repositories, &repo)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func getRepository(id int) (r *Repository, err error) {
r = new(Repository)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_repository=?", id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef, &r.LastTests, &r.TestsRef)
return
}
func (u *User) getRepository(id int) (r *Repository, err error) {
r = new(Repository)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_repository=? AND id_user=?", id, u.Id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef, &r.LastTests, &r.TestsRef)
return
}
func (u *User) getRepositoryByWork(id int64) (r *Repository, err error) {
r = new(Repository)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_work=? AND id_user=? ORDER BY last_tests DESC LIMIT 1", id, u.Id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef, &r.LastTests, &r.TestsRef)
return
}
func (u *User) NewRepository(w *Work, uri string) (*Repository, error) {
secret := make([]byte, 24)
_, err := rand.Read(secret)
if err != nil {
return nil, err
}
if res, err := DBExec("INSERT INTO user_work_repositories (id_user, id_work, uri, secret, droneref, testsref) VALUES (?, ?, ?, ?, ?, '')", u.Id, w.Id, uri, secret, ""); err != nil {
return nil, err
} else if rid, err := res.LastInsertId(); err != nil {
return nil, err
} else {
return &Repository{rid, u.Id, w.Id, uri, secret, nil, "", nil, "", false}, nil
}
}
func (r *Repository) Update() (*Repository, error) {
if _, err := DBExec("UPDATE user_work_repositories SET id_user = ?, id_work = ?, uri = ?, secret = ?, last_check = ?, droneref = ?, last_tests = ?, testsref = ? WHERE id_repository = ?", r.IdUser, r.IdWork, r.URI, r.Secret, r.LastCheck, r.DroneRef, r.LastTests, r.TestsRef, r.Id); err != nil {
return nil, err
} else {
return r, err
}
}
func (r Repository) Delete() (int64, error) {
if res, err := DBExec("DELETE FROM user_work_repositories WHERE id_repository = ?", r.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
func ClearRepositories() (int64, error) {
if res, err := DBExec("DELETE FROM user_work_repositories"); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}