My Very Own CI-server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

456 lines
14 KiB

package web
import (
"fmt"
"strings"
"strconv"
//"regexp"
"net/http"
"mvoCI/core"
"mvoCI/build"
//"golang.org/x/crypto/bcrypt"
//"github.com/jinzhu/gorm"
"github.com/labstack/echo"
//"github.com/labstack/echo/middleware"
)
type repo_state struct {
R core.Repository
State string
}
func repoHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}
var r []core.Repository;
if ( user.Superuser ) {
s.db.Order("name asc").Find ( &r );
} else {
s.db.Where("user_id = ?", user.ID).Order("name asc").Find ( &r );
}
var rx []repo_state;
for _,v := range r {
var bx core.Build
s.db.Where ( "repository_id = ?", v.ID).Order("started_at DESC").Limit(1).First( &bx );
rx = append ( rx, repo_state { R: v, State: strings.ToLower ( bx.Status ) } )
}
return ctx.Render ( http.StatusOK, "repo", echo.Map{
"navPage": "repo",
"user": user,
"repositories": rx,
"u": nil,
})
}
func repoStateHandler ( ctx echo.Context ) error {
id, err := strconv.Atoi(ctx.Param ("id"));
if err == nil {
var r core.Repository;
s.db.Select ( "id,public" ).Where ("id = ?", id).First ( &r );
if r.Public == true {
var b core.Build;
s.db.Select ( "id, status" ).Where("repository_id = ? AND status IN ('finished', 'failed')", r.ID).Order("finished_at DESC").Limit(1).First(&b);
if b.ID > 0 {
if b.Status == "finished" {
return ctx.File ( "static/img/build_passed.svg" );
} else if b.Status == "failed" {
return ctx.File ( "static/img/build_miss.svg" );
}
}
}
}
return ctx.File ( "static/img/build_miss.svg" );
}
func repoEditHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}
id, _ := strconv.Atoi(ctx.Param("id"))
var r core.Repository;
var bs []core.BuildScript;
s.db.Where("id = ?", strconv.Itoa(id)).First ( &r )
s.db.Where("repository_id = ?", r.ID).Find( &bs );
if r.ID <= 0 {
return HTTPError ( 404, ctx );
}
if user.Superuser || r.UserID == user.ID {
fmt.Println ("Length: ", len(bs))
var pbs, rbs string
for _,v := range bs {
if v.EventType == "push" {
pbs = v.ShellScript;
}
if v.EventType == "release" {
rbs = v.ShellScript;
}
}
fmt.Println ( pbs, rbs)
return ctx.Render ( http.StatusOK, "repo_edit", echo.Map{
"mode": "edit",
"navPage": "repo",
"user": user,
"repo": r,
"errors": []string{},
"success": []string{},
"PushBuildScript": pbs,
"ReleaseBuildScript": rbs,
})
} else {
return ctx.Redirect ( http.StatusFound, "/repo" )
}
}
func repoBuildHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}
var r core.Repository
id, _ := strconv.Atoi(ctx.Param("id"))
s.db.Where("id = ?", id).First ( &r )
if r.ID > 0 {
if user.Superuser == false && r.UserID != user.ID {
return HTTPError ( 401, ctx );
}
b := build.BuildNow ( r, s.db )
return ctx.Redirect ( http.StatusFound, "/build/"+strconv.Itoa(int(b)) )
}
return ctx.Redirect ( http.StatusFound, "/repo/view/"+strconv.Itoa(id) )
}
func repoAddHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}
return ctx.Render ( http.StatusOK, "repo_edit", echo.Map{
"mode": "add",
"navPage": "repo",
"user": user,
"repo": core.Repository{},
"errors": []string{},
"success": []string{},
})
}
func repoViewHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}
id, _ := strconv.Atoi(ctx.Param("id"))
page, _ := strconv.Atoi(ctx.Param("page"))
if page <= 0 {
page = 1;
}
offset := (page-1)*10;
var r core.Repository;
var u core.User;
s.db.Where("id = ?", strconv.Itoa(id)).First ( &r )
if r.ID <= 0 {
return HTTPError ( 404, ctx );
}
// auth check
if r.UserID != user.ID && user.Superuser == false {
ctx.Redirect ( http.StatusFound, "/repo" )
}
s.db.Select("name, id").Where("id = ?", r.UserID).First ( &u )
var cnt int;
s.db.Model( core.Build{} ).Where("repository_id = ?", r.ID).Count ( &cnt )
var pageMax int = (cnt / 10) + 1
if offset >= cnt {
offset = (cnt / 10) * 10;
page = (offset / 10) + 1
}
cnt = 0;
var b []core.Build;
var bx core.Build;
s.db.Where("repository_id = ?", r.ID).Order("started_at DESC").Offset( (page-1)*10 ).Limit(10).Find ( &b )
s.db.Select ("id").Where("repository_id = ? AND status NOT IN ('finished', 'failed')", r.ID ).First ( &bx )
var pageRange []int
if pageMax > 9 {
pageRange = make([]int, 11)
pageRange[0] = 1;
pageRange[1] = 2;
pageRange[2] = 3;
if page == 4 || page == 5 {
pageRange[3] = 4;
pageRange[4] = 5;
pageRange[5] = 6;
pageRange[6] = 7;
pageRange[7] = -1; // => ...
} else if page == pageMax - 4 || page == pageMax - 3 {
pageRange[3] = -1;
pageRange[4] = pageMax - 6;
pageRange[5] = pageMax - 5;
pageRange[6] = pageMax - 4;
pageRange[7] = pageMax - 3;
} else {
pageRange[3] = -1;
if page < pageMax - 4 && page > 5 {
pageRange[4] = page - 1
pageRange[5] = page
pageRange[6] = page + 1
} else {
pageRange[4] = pageMax/2
pageRange[5] = pageMax/2 + 1
pageRange[6] = pageMax/2 + 2
}
pageRange[7] = -1
}
pageRange[8] = pageMax - 2;
pageRange[9] = pageMax - 1;
pageRange[10] = pageMax
} else {
pageRange = make([]int, pageMax)
var i int = 1;
for i <= pageMax {
pageRange[i-1] = i;
i++;
}
}
return ctx.Render ( http.StatusOK, "repo_view", echo.Map{
"navPage": "repo",
"user": user,
"u": u,
"repo": r,
"builds": b,
"unfinishedBuild": bx.ID,
"page" : page,
"pageMax" : pageMax,
"pageRange": pageRange,
})
}
func repoPostChecker ( ctx echo.Context, user core.User, edit bool ) ([]string, []string, uint) {
var name string;
var cloneUrl string;
var defaultBranch string;
var pushBuildScript string;
var releaseBuildScript string
lb := false
wh := false
keepBuilds := false;
public := false;
var id int;
var err error;
id = -1;
if edit == true {
id, err = strconv.Atoi(ctx.Param ("id"))
if err != nil {
return []string{"Invalid ID"},[]string{},0
}
}
name = ctx.FormValue ("repo_name")
cloneUrl = ctx.FormValue ("repo_clone_url")
defaultBranch = ctx.FormValue ("repo_default_branch")
pushBuildScript = ctx.FormValue ("repo_push_build_script")
releaseBuildScript = ctx.FormValue ( "repo_release_build_script" )
if ctx.FormValue ("repo_public_enable") == "on" {
public = true
}
if ctx.FormValue ("repo_keepbuilds_enable") == "on" {
keepBuilds = true
}
if ctx.FormValue("repo_webhook_enable") == "on" {
wh = true
}
if ctx.FormValue ("repo_localbuild_enable") == "on" {
lb = true
}
if len(defaultBranch) <= 0 {
defaultBranch = "master"
}
if len(name) <= 0 || len(cloneUrl) <= 0 || len(pushBuildScript) <= 0 {
return []string{"Did you input anything?"},[]string{},0
}
/*var cnt int;
s.db.Model( &core.Repository{} ).Where("name = ?", name).Count ( &cnt );
if cnt >= 1 {
return []string{"A repository with the same name already exists"}, []string{}, 0
}*/
// TODO check if repo exists and if I can read from it!
var secret string;
var r core.Repository
if edit == false {
var x int = 1
for x > 0 {
secret = authSecretGenerator()
s.db.Model ( &core.Repository{} ).Where ("secret = ?", secret ).Count ( &x )
}
bp := core.BuildScript { EventType: "push", ShellScript: pushBuildScript };
br := core.BuildScript { EventType: "release", ShellScript: releaseBuildScript };
s.db.Save ( &bp );
s.db.Save ( &br );
r = core.Repository{ Name: name, CloneUrl: cloneUrl,
DefaultBranch : defaultBranch, Secret: secret,
BuildCount: 0, BuildScripts: []core.BuildScript{ bp, br },
WebHookEnable: wh, LocalBuildEnable: lb,
KeepBuilds: keepBuilds, Public: public,
User: user }
s.db.Save ( &r )
if r.ID <= 0 {
return []string{"Could not create the repository"},[]string{},0
}
} else {
secret = ctx.FormValue ( "repo_secret")
s.db.Where ("id = ?", id).First ( &r )
br := core.BuildScript{};
bp := core.BuildScript{};
s.db.Where("repository_id = ? AND event_type = ?", r.ID, "push").First(&bp);
s.db.Where("repository_id = ? AND event_type = ?", r.ID, "release").First(&br);
if bp.ID <= 0 {
bp.EventType = "push";
r.BuildScripts = append ( r.BuildScripts, bp );
}
if br.ID <= 0 {
br.EventType = "release";
r.BuildScripts = append ( r.BuildScripts, br );
}
if r.ID <= 0 {
return []string{"Invalid ID"},[]string{},0
}
if !user.Superuser && user.ID != r.UserID {
return []string{"No permission"},[]string{},0
}
r.WebHookEnable = wh
r.LocalBuildEnable = lb
r.Name = name
r.CloneUrl = cloneUrl
r.DefaultBranch = defaultBranch
r.Secret = secret
//r.BuildScript = buildScript
r.KeepBuilds = keepBuilds
r.Public = public
br.ShellScript = releaseBuildScript
bp.ShellScript = pushBuildScript
err = s.db.Save ( &r ).Error
if err != nil {
return []string{err.Error()}, []string{}, r.ID
}
err = s.db.Save ( &bp ).Error
if err != nil {
return []string{err.Error()}, []string{}, r.ID
}
err = s.db.Save ( &br ).Error
if err != nil {
return []string{err.Error()}, []string{}, r.ID
}
}
return []string{}, []string{"ok"}, r.ID
}
func repoEditPostHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}
err, _, rid := repoPostChecker ( ctx, user, true );
id, err2 := strconv.Atoi( ctx.Param ("id") )
if len(err) > 0 {
// the edit operation did not work, no trustworthy id in rid
if err2 == nil {
var r core.Repository
s.db.Select("id").Where ("id = ?", strconv.Itoa(id)).First (&r);
if r.ID > 0 {
return ctx.Redirect ( http.StatusFound, "/repo/edit/" + strconv.Itoa(id) )
}
return ctx.Render ( http.StatusOK, "repo_edit", echo.Map{
"mode": "add",
"navPage": "repo",
"user": user,
"u": core.Repository{},
"errors": err,
"success": []string{},
})
}
}
return ctx.Redirect ( http.StatusFound, "/repo/view/"+strconv.FormatUint( uint64(rid), 10 ) )
}
func repoAddPostHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}
err, _, rid := repoPostChecker ( ctx, user, false );
if len(err) > 0 {
return ctx.Render ( http.StatusOK, "repo_edit", echo.Map{
"mode": "add",
"navPage": "repo",
"user": user,
"u": core.Repository{},
"errors": err,
"success": []string{},
})
}
return ctx.Redirect ( http.StatusFound, "/repo/view/"+strconv.FormatUint(uint64(rid), 10) )
}
func repoDeleteHandler ( ctx echo.Context ) error {
user := core.User{};
if UserFromSession ( &user, ctx ) != true {
return loginHandler ( ctx );
}
id, err := strconv.Atoi ( ctx.Param ("id") )
if err != nil {
// todo: error propagation
return ctx.Redirect ( http.StatusFound, "/repo");
}
var r core.Repository;
var builds []core.Build;
s.db.Where("id = ?", id).First(&r);
if user.Superuser != true || r.UserID != user.ID {
HTTPError ( 401, ctx );
}
// delete all builds
s.db.Select("id").Where("repository_id = ?", id).Find( &builds )
for _, b := range builds {
buildDelete ( b.ID )
}
s.db.Delete(&r);
// delete all build-scripts
s.db.Select("id").Where("repository_id = ?", id).Delete ( &core.BuildScript{} )
return ctx.Redirect ( http.StatusFound, "/repo");
}