Browse Source

Linting, vet, misspell, also update drone ci

tags/v0.3
Dashie der otter 3 years ago
parent
commit
0f93bfd75f
Signed by: dashie <rhaamo@leloop.org> GPG Key ID: C2D57B325840B755
47 changed files with 479 additions and 301 deletions
  1. +3
    -0
      .drone.yml
  2. +21
    -2
      Makefile
  3. +7
    -6
      cmd/web.go
  4. +2
    -0
      context/auth.go
  5. +41
    -38
      context/context.go
  6. +4
    -0
      context/repo.go
  7. +1
    -0
      context/user.go
  8. +3
    -3
      git.txt.go
  9. +42
    -2
      models/error.go
  10. +3
    -0
      models/errors/repo.go
  11. +9
    -0
      models/errors/user.go
  12. +29
    -22
      models/gitxt.go
  13. +19
    -9
      models/models.go
  14. +9
    -6
      models/sshkey.go
  15. +38
    -27
      models/user.go
  16. +3
    -2
      routers/admin/admin.go
  17. +35
    -30
      routers/gitxt/gitxt.go
  18. +1
    -0
      routers/hub.go
  19. +1
    -0
      routers/repo/archive.go
  20. +22
    -17
      routers/repo/repo.go
  21. +32
    -27
      routers/user/auth.go
  22. +5
    -3
      routers/user/setting.go
  23. +31
    -16
      setting/setting.go
  24. +1
    -0
      stuff/auth/auth.go
  25. +1
    -0
      stuff/cron/cron.go
  26. +4
    -2
      stuff/form/auth.go
  27. +6
    -5
      stuff/form/form.go
  28. +6
    -3
      stuff/form/gitxt.go
  29. +2
    -1
      stuff/form/user.go
  30. +29
    -25
      stuff/gite/gite.go
  31. +17
    -10
      stuff/mailer/mail.go
  32. +8
    -1
      stuff/mailer/mailer.go
  33. +3
    -3
      stuff/markup/markdown.go
  34. +6
    -12
      stuff/markup/markup.go
  35. +6
    -5
      stuff/repository/repository.go
  36. +2
    -1
      stuff/sanitize/sanitize.go
  37. +1
    -1
      stuff/sanitize/sanitize_test.go
  38. +1
    -0
      stuff/template/highlight/highlight.go
  39. +12
    -11
      stuff/template/template.go
  40. +1
    -0
      stuff/tool/file.go
  41. +4
    -3
      stuff/tool/tool.go
  42. +1
    -1
      vendor/github.com/go-macaron/binding/multipart_test.go
  43. +2
    -2
      vendor/github.com/go-xorm/xorm/engine.go
  44. +1
    -1
      vendor/golang.org/x/net/http2/server_test.go
  45. +2
    -2
      vendor/golang.org/x/text/encoding/internal/identifier/mib.go
  46. +1
    -1
      vendor/gopkg.in/libgit2/git2go.v25/note.go
  47. +1
    -1
      vendor/gopkg.in/libgit2/git2go.v25/odb.go

+ 3
- 0
.drone.yml View File

@@ -18,6 +18,9 @@ pipeline:
- go get -u github.com/golang/dep/cmd/dep
- dep ensure
- make clean
- make vet
- make lint
- make misspell-check
- make build
when:
event: [ push, tag, pull_request ]


+ 21
- 2
Makefile View File

@@ -8,8 +8,10 @@ DATA_FILES := $(shell find conf | sed 's/ /\\ /g')
BUILD_FLAGS:=-o git_txt -v
TAGS=sqlite
NOW=$(shell date -u '+%Y%m%d%I%M%S')
GOVET=go tool vet -composites=false -methods=false -structtags=false
GOVET=go vet
GOLINT=golint -set_exit_status

GOFILES := $(shell find . -name "*.go" -type f ! -path "./vendor/*" ! -path "*/bindata.go")
PACKAGES ?= $(filter-out dev.sigpipe.me/dashie/git.txt/integrations,$(shell go list ./... | grep -v /vendor/))

.PHONY: build clean
@@ -21,9 +23,12 @@ check: test
web: build
./git_txt web

govet:
vet:
$(GOVET) git.txt.go

lint:
$(GOLINT) $(PACKAGES)

build:
go build $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(TAGS)'

@@ -41,3 +46,17 @@ clean-mac: clean

test:
go test -cover -v $(PACKAGES)

.PHONY: misspell-check
misspell-check:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -error -i unknwon $(GOFILES)

.PHONY: misspell
misspell:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -w -i unknwon $(GOFILES)

+ 7
- 6
cmd/web.go View File

@@ -31,6 +31,7 @@ import (
"dev.sigpipe.me/dashie/git.txt/stuff/mailer"
)

// Web command
var Web = cli.Command{
Name: "web",
Usage: "Start web server",
@@ -50,7 +51,7 @@ func newMacaron() *macaron.Macaron {

m.Use(macaron.Recovery())

if setting.Protocol == setting.SCHEME_FCGI {
if setting.Protocol == setting.SchemeFCGI {
m.SetURLPrefix(setting.AppSubURL)
}

@@ -212,7 +213,7 @@ func runWeb(ctx *cli.Context) error {
}

var listenAddr string
if setting.Protocol == setting.SCHEME_UNIX_SOCKET {
if setting.Protocol == setting.SchemeUnixSocket {
listenAddr = fmt.Sprintf("%s", setting.HTTPAddr)
} else {
listenAddr = fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.HTTPPort)
@@ -221,13 +222,13 @@ func runWeb(ctx *cli.Context) error {

var err error
switch setting.Protocol {
case setting.SCHEME_HTTP:
case setting.SchemeHTTP:
err = http.ListenAndServe(listenAddr, m)
case setting.SCHEME_HTTPS:
case setting.SchemeHTTPS:
log.Fatal(2, "https not supported")
case setting.SCHEME_FCGI:
case setting.SchemeFCGI:
err = fcgi.Serve(nil, m)
case setting.SCHEME_UNIX_SOCKET:
case setting.SchemeUnixSocket:
os.Remove(listenAddr)

var listener *net.UnixListener


+ 2
- 0
context/auth.go View File

@@ -8,6 +8,7 @@ import (
log "gopkg.in/clog.v1"
)

// ToggleOptions struct
type ToggleOptions struct {
SignInRequired bool
SignOutRequired bool
@@ -16,6 +17,7 @@ type ToggleOptions struct {
AnonymousCreate bool
}

// Toggle options
func Toggle(options *ToggleOptions) macaron.Handler {
return func(ctx *Context) {
// Redirect non-login pages from logged in user


+ 41
- 38
context/context.go View File

@@ -47,9 +47,9 @@ func (c *Context) PageIs(name string) {
}

// HTML responses template with given status.
func (ctx *Context) HTML(status int, name string) {
func (c *Context) HTML(status int, name string) {
log.Trace("Template: %s", name)
ctx.Context.HTML(status, name)
c.Context.HTML(status, name)
}

// Success responses template with status http.StatusOK.
@@ -63,45 +63,46 @@ func (c *Context) JSONSuccess(data interface{}) {
}

// HasError returns true if error occurs in form validation.
func (ctx *Context) HasError() bool {
hasErr, ok := ctx.Data["HasError"]
func (c *Context) HasError() bool {
hasErr, ok := c.Data["HasError"]
if !ok {
return false
}
ctx.Flash.ErrorMsg = ctx.Data["ErrorMsg"].(string)
ctx.Data["Flash"] = ctx.Flash
c.Flash.ErrorMsg = c.Data["ErrorMsg"].(string)
c.Data["Flash"] = c.Flash
return hasErr.(bool)
}

// RenderWithErr used for page has form validation but need to prompt error to users.
func (ctx *Context) RenderWithErr(msg, tpl string, f interface{}) {
func (c *Context) RenderWithErr(msg, tpl string, f interface{}) {
if f != nil {
form.Assign(f, ctx.Data)
form.Assign(f, c.Data)
}
ctx.Flash.ErrorMsg = msg
ctx.Data["Flash"] = ctx.Flash
ctx.HTML(http.StatusOK, tpl)
c.Flash.ErrorMsg = msg
c.Data["Flash"] = c.Flash
c.HTML(http.StatusOK, tpl)
}

// Handle handles and logs error by given status.
func (ctx *Context) Handle(status int, title string, err error) {
func (c *Context) Handle(status int, title string, err error) {
switch status {
case http.StatusNotFound:
ctx.Data["Title"] = ctx.Tr("error.page_not_found")
c.Data["Title"] = c.Tr("error.page_not_found")
case http.StatusInternalServerError:
ctx.Data["Title"] = ctx.Tr("internal_server_error")
c.Data["Title"] = c.Tr("internal_server_error")
log.Error(2, "%s: %v", title, err)
}
ctx.HTML(status, fmt.Sprintf("status/%d", status))
c.HTML(status, fmt.Sprintf("status/%d", status))
}

func (ctx *Context) HandleText(status int, title string) {
ctx.PlainText(status, []byte(title))
// HandleText only
func (c *Context) HandleText(status int, title string) {
c.PlainText(status, []byte(title))
}

// NotFound renders the 404 page.
func (ctx *Context) NotFound() {
ctx.Handle(http.StatusNotFound, "", nil)
func (c *Context) NotFound() {
c.Handle(http.StatusNotFound, "", nil)
}

// ServerError renders the 500 page.
@@ -109,7 +110,7 @@ func (c *Context) ServerError(title string, err error) {
c.Handle(http.StatusInternalServerError, title, err)
}

// SubURLRedirect responses redirection wtih given location and status.
// SubURLRedirect responses redirection with given location and status.
// It prepends setting.AppSubURL to the location string.
func (c *Context) SubURLRedirect(location string, status ...int) {
c.Redirect(setting.AppSubURL + location)
@@ -126,7 +127,8 @@ func (c *Context) NotFoundOrServerError(title string, errck func(error) bool, er
c.ServerError(title, err)
}

func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
// ServeContent headers
func (c *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
modtime := time.Now()
for _, p := range params {
switch v := p.(type) {
@@ -134,17 +136,18 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa
modtime = v
}
}
ctx.Resp.Header().Set("Content-Description", "File Transfer")
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
ctx.Resp.Header().Set("Expires", "0")
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
ctx.Resp.Header().Set("Pragma", "public")
http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r)
}

func (ctx *Context) ServeContentNoDownload(name string, mime string, r io.ReadSeeker, params ...interface{}) {
c.Resp.Header().Set("Content-Description", "File Transfer")
c.Resp.Header().Set("Content-Type", "application/octet-stream")
c.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
c.Resp.Header().Set("Content-Transfer-Encoding", "binary")
c.Resp.Header().Set("Expires", "0")
c.Resp.Header().Set("Cache-Control", "must-revalidate")
c.Resp.Header().Set("Pragma", "public")
http.ServeContent(c.Resp, c.Req.Request, name, modtime, r)
}

// ServeContentNoDownload headers
func (c *Context) ServeContentNoDownload(name string, mime string, r io.ReadSeeker, params ...interface{}) {
modtime := time.Now()
for _, p := range params {
switch v := p.(type) {
@@ -152,12 +155,12 @@ func (ctx *Context) ServeContentNoDownload(name string, mime string, r io.ReadSe
modtime = v
}
}
ctx.Resp.Header().Set("Content-Description", "File Content")
ctx.Resp.Header().Set("Content-Type", mime)
ctx.Resp.Header().Set("Expires", "0")
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
ctx.Resp.Header().Set("Pragma", "public")
http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r)
c.Resp.Header().Set("Content-Description", "File Content")
c.Resp.Header().Set("Content-Type", mime)
c.Resp.Header().Set("Expires", "0")
c.Resp.Header().Set("Cache-Control", "must-revalidate")
c.Resp.Header().Set("Pragma", "public")
http.ServeContent(c.Resp, c.Req.Request, name, modtime, r)
}

// Contexter initializes a classic context for a request.


+ 4
- 0
context/repo.go View File

@@ -10,6 +10,7 @@ import (
log "gopkg.in/clog.v1"
)

// Gitxt struct
type Gitxt struct {
User *models.User
Gitxt *models.Gitxt
@@ -17,6 +18,7 @@ type Gitxt struct {
UserName string
}

// AssignRepository to context
func AssignRepository() macaron.Handler {
return func(ctx *Context) {
userName := ctx.Params("user")
@@ -41,6 +43,7 @@ func AssignRepository() macaron.Handler {
}
}

// GitUACheck User Agent check
func GitUACheck() macaron.Handler {
return func(ctx *Context) {
if strings.HasPrefix(strings.Join(ctx.Req.Header["User-Agent"], ""), "git/") {
@@ -51,6 +54,7 @@ func GitUACheck() macaron.Handler {
}
}

// CheckRepoExpiry and handle
func CheckRepoExpiry() macaron.Handler {
return func(ctx *Context) {
if ctx.Gitxt.Gitxt.ExpiryHours > 0 && ctx.Gitxt.Gitxt.ExpiryUnix <= time.Now().Unix() {


+ 1
- 0
context/user.go View File

@@ -6,6 +6,7 @@ import (
"dev.sigpipe.me/dashie/git.txt/models/errors"
)

// AssignUser to context
func AssignUser() macaron.Handler {
return func(ctx *Context) {
userName := ctx.Params("user")


+ 3
- 3
git.txt.go View File

@@ -9,10 +9,10 @@ import (
"fmt"
)

const APP_VER = "0.2"
const appVersion = "0.2"

func init() {
setting.AppVer = APP_VER
setting.AppVer = appVersion
if os.Getenv("USE_RAVEN") == "true" {
raven.SetDSN(os.Getenv("RAVEN_DSN"))
fmt.Printf("Using Raven with DSN: %s\r\n", os.Getenv("RAVEN_DSN"))
@@ -25,7 +25,7 @@ func main() {
app := cli.NewApp()
app.Name = "git.txt"
app.Usage = "paste stuff to the interweb with git backend"
app.Version = APP_VER
app.Version = appVersion
app.Commands = []cli.Command{
cmd.Web,
}


+ 42
- 2
models/error.go View File

@@ -8,28 +8,34 @@ import (
"fmt"
)

// ErrNameReserved struct
type ErrNameReserved struct {
Name string
}

// IsErrNameReserved func
func IsErrNameReserved(err error) bool {
_, ok := err.(ErrNameReserved)
return ok
}

// Error func
func (err ErrNameReserved) Error() string {
return fmt.Sprintf("name is reserved [name: %s]", err.Name)
}

// ErrNamePatternNotAllowed struct
type ErrNamePatternNotAllowed struct {
Pattern string
}

// IsErrNamePatternNotAllowed func
func IsErrNamePatternNotAllowed(err error) bool {
_, ok := err.(ErrNamePatternNotAllowed)
return ok
}

// Error func
func (err ErrNamePatternNotAllowed) Error() string {
return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern)
}
@@ -41,41 +47,50 @@ func (err ErrNamePatternNotAllowed) Error() string {
// |______//____ >\___ >__|
// \/ \/

// ErrUserAlreadyExist struct
type ErrUserAlreadyExist struct {
Name string
}

// IsErrUserAlreadyExist func
func IsErrUserAlreadyExist(err error) bool {
_, ok := err.(ErrUserAlreadyExist)
return ok
}

// Error func
func (err ErrUserAlreadyExist) Error() string {
return fmt.Sprintf("user already exists [name: %s]", err.Name)
}

// ErrEmailAlreadyUsed struct
type ErrEmailAlreadyUsed struct {
Email string
}

// IsErrEmailAlreadyUsed func
func IsErrEmailAlreadyUsed(err error) bool {
_, ok := err.(ErrEmailAlreadyUsed)
return ok
}

// Error func
func (err ErrEmailAlreadyUsed) Error() string {
return fmt.Sprintf("e-mail has been used [email: %s]", err.Email)
}

// ErrUserOwnRepos struct
type ErrUserOwnRepos struct {
UID int64
}

// IsErrUserOwnRepos func
func IsErrUserOwnRepos(err error) bool {
_, ok := err.(ErrUserOwnRepos)
return ok
}

// Error func
func (err ErrUserOwnRepos) Error() string {
return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID)
}
@@ -87,131 +102,156 @@ func (err ErrUserOwnRepos) Error() string {
// |____| |____/|___ /____/__|\___ > |____|__ \___ > ____|
// \/ \/ \/ \/\/

// ErrKeyUnableVerify struct
type ErrKeyUnableVerify struct {
Result string
}

// IsErrKeyUnableVerify func
func IsErrKeyUnableVerify(err error) bool {
_, ok := err.(ErrKeyUnableVerify)
return ok
}

// Error func
func (err ErrKeyUnableVerify) Error() string {
return fmt.Sprintf("Unable to verify key content [result: %s]", err.Result)
}

// ErrKeyNotExist struct
type ErrKeyNotExist struct {
ID int64
}

// IsErrKeyNotExist func
func IsErrKeyNotExist(err error) bool {
_, ok := err.(ErrKeyNotExist)
return ok
}

// Error func
func (err ErrKeyNotExist) Error() string {
return fmt.Sprintf("public key does not exist [id: %d]", err.ID)
}

// ErrKeyAlreadyExist struct
type ErrKeyAlreadyExist struct {
OwnerID int64
Content string
}

// IsErrKeyAlreadyExist func
func IsErrKeyAlreadyExist(err error) bool {
_, ok := err.(ErrKeyAlreadyExist)
return ok
}

// Error func
func (err ErrKeyAlreadyExist) Error() string {
return fmt.Sprintf("public key already exists [owner_id: %d, content: %s]", err.OwnerID, err.Content)
}

// ErrKeyNameAlreadyUsed struct
type ErrKeyNameAlreadyUsed struct {
OwnerID int64
Name string
}

// IsErrKeyNameAlreadyUsed func
func IsErrKeyNameAlreadyUsed(err error) bool {
_, ok := err.(ErrKeyNameAlreadyUsed)
return ok
}

// Error func
func (err ErrKeyNameAlreadyUsed) Error() string {
return fmt.Sprintf("public key already exists [owner_id: %d, name: %s]", err.OwnerID, err.Name)
}

// ErrKeyAccessDenied struct
type ErrKeyAccessDenied struct {
UserID int64
KeyID int64
Note string
}

// IsErrKeyAccessDenied func
func IsErrKeyAccessDenied(err error) bool {
_, ok := err.(ErrKeyAccessDenied)
return ok
}

// Error func
func (err ErrKeyAccessDenied) Error() string {
return fmt.Sprintf("user does not have access to the key [user_id: %d, key_id: %d, note: %s]",
err.UserID, err.KeyID, err.Note)
}

// ErrDeployKeyNotExist struct
type ErrDeployKeyNotExist struct {
ID int64
KeyID int64
RepoID int64
}

// IsErrDeployKeyNotExist func
func IsErrDeployKeyNotExist(err error) bool {
_, ok := err.(ErrDeployKeyNotExist)
return ok
}

// Error func
func (err ErrDeployKeyNotExist) Error() string {
return fmt.Sprintf("Deploy key does not exist [id: %d, key_id: %d, repo_id: %d]", err.ID, err.KeyID, err.RepoID)
}

// ErrDeployKeyAlreadyExist struct
type ErrDeployKeyAlreadyExist struct {
KeyID int64
RepoID int64
}

// IsErrDeployKeyAlreadyExist func
func IsErrDeployKeyAlreadyExist(err error) bool {
_, ok := err.(ErrDeployKeyAlreadyExist)
return ok
}

// Error func
func (err ErrDeployKeyAlreadyExist) Error() string {
return fmt.Sprintf("public key already exists [key_id: %d, repo_id: %d]", err.KeyID, err.RepoID)
}

// ErrDeployKeyNameAlreadyUsed struct
type ErrDeployKeyNameAlreadyUsed struct {
RepoID int64
Name string
}

// IsErrDeployKeyNameAlreadyUsed func
func IsErrDeployKeyNameAlreadyUsed(err error) bool {
_, ok := err.(ErrDeployKeyNameAlreadyUsed)
return ok
}

// Error func
func (err ErrDeployKeyNameAlreadyUsed) Error() string {
return fmt.Sprintf("public key already exists [repo_id: %d, name: %s]", err.RepoID, err.Name)
}


// Gitxt
// ErrHashAlreadyExist struct
type ErrHashAlreadyExist struct {
Hash string
}

// IsErrHashAlreadyExist func
func IsErrHashAlreadyExist(err error) bool {
_, ok := err.(ErrHashAlreadyExist)
return ok
}

// Error func
func (err ErrHashAlreadyExist) Error() string {
return fmt.Sprintf("hash already exists [hash: %s]", err.Hash)
}

+ 3
- 0
models/errors/repo.go View File

@@ -2,17 +2,20 @@ package errors

import "fmt"

// RepoNotExist struct
type RepoNotExist struct {
ID int64
UserID int64
Name string
}

// IsRepoNotExist func
func IsRepoNotExist(err error) bool {
_, ok := err.(RepoNotExist)
return ok
}

// Error func
func (err RepoNotExist) Error() string {
return fmt.Sprintf("repository does not exist [id: %d, user_id: %d, name: %s]", err.ID, err.UserID, err.Name)
}

+ 9
- 0
models/errors/user.go View File

@@ -6,40 +6,49 @@ package errors

import "fmt"

// EmptyName struct
type EmptyName struct{}

// IsEmptyName func
func IsEmptyName(err error) bool {
_, ok := err.(EmptyName)
return ok
}

// Error func
func (err EmptyName) Error() string {
return "empty name"
}

// UserNotExist struct
type UserNotExist struct {
UserID int64
Name string
}

// IsUserNotExist func
func IsUserNotExist(err error) bool {
_, ok := err.(UserNotExist)
return ok
}

// Error func
func (err UserNotExist) Error() string {
return fmt.Sprintf("user does not exist [user_id: %d, name: %s]", err.UserID, err.Name)
}

// UserNotKeyOwner struct
type UserNotKeyOwner struct {
KeyID int64
}

// IsUserNotKeyOwner func
func IsUserNotKeyOwner(err error) bool {
_, ok := err.(UserNotKeyOwner)
return ok
}

// Error func
func (err UserNotKeyOwner) Error() string {
return fmt.Sprintf("user is not the owner of public key [key_id: %d]", err.KeyID)
}

+ 29
- 22
models/gitxt.go View File

@@ -16,6 +16,7 @@ import (
"os/exec"
)

// Gitxt struct
type Gitxt struct {
ID int64 `xorm:"pk autoincr"`
Hash string `xorm:"UNIQUE NOT NULL"`
@@ -23,7 +24,7 @@ type Gitxt struct {
Anonymous bool
Description string `xorm:"TEXT"`

// Choosen expiry in hours
// Chosen expiry in hours
ExpiryHours int64 `xorm:"INDEX"`
// Calculated expiry unix timestamp from the time of creation/update
ExpiryUnix int64
@@ -41,26 +42,30 @@ type Gitxt struct {
// UserID
}

// BeforeInsert hooks
func (gitxt *Gitxt) BeforeInsert() {
gitxt.CreatedUnix = time.Now().Unix()
gitxt.UpdatedUnix = gitxt.CreatedUnix
}

// BeforeUpdate hooks
func (gitxt *Gitxt) BeforeUpdate() {
gitxt.UpdatedUnix = time.Now().Unix()
}

func (g *Gitxt) AfterSet(colName string, _ xorm.Cell) {
// AfterSet hooks
func (gitxt *Gitxt) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
g.Created = time.Unix(g.CreatedUnix, 0).Local()
gitxt.Created = time.Unix(gitxt.CreatedUnix, 0).Local()
case "updated_unix":
g.Updated = time.Unix(g.UpdatedUnix, 0).Local()
gitxt.Updated = time.Unix(gitxt.UpdatedUnix, 0).Local()
case "expiry_unix":
g.Expiry = time.Unix(g.ExpiryUnix, 0).Local()
gitxt.Expiry = time.Unix(gitxt.ExpiryUnix, 0).Local()
}
}

// GitxtWithUser struct
type GitxtWithUser struct {
User `xorm:"extends"`
Gitxt `xorm:"extends"`
@@ -70,8 +75,8 @@ type GitxtWithUser struct {
var taskStatusTable = sync.NewStatusTable()

const (
_CLEAN_OLD_ARCHIVES = "clean_old_archives"
_DELETE_EXPIRED_REPOSITORIES = "delete_expired_repositories"
cleanOldArchives = "clean_old_archives"
deleteExpiredRepositories = "delete_expired_repositories"
)

// IsHashUsed checks if given hash exist,
@@ -82,7 +87,7 @@ func IsHashUsed(uid int64, hash string) (bool, error) {
return x.Get(&Gitxt{Hash: hash})
}

// Create a new gitxt
// CreateGitxt Create a new gitxt
func CreateGitxt(g *Gitxt) (err error) {
isExist, err := IsHashUsed(0, g.Hash)
if err != nil {
@@ -131,6 +136,7 @@ func GetRepositoryByName(user string, name string) (*Gitxt, error) {
return repo, nil
}

// GitxtOptions struct
type GitxtOptions struct {
UserID int64
WithPrivate bool
@@ -139,7 +145,7 @@ type GitxtOptions struct {
PageSize int
}

// Get gitxts
// GetGitxts Get gitxts
func GetGitxts(opts *GitxtOptions) (gitxts []*GitxtWithUser, _ int64, _ error) {
if opts.Page <= 0 {
opts.Page = 1
@@ -176,23 +182,24 @@ func updateGitxt(e Engine, u *Gitxt) error {
return err
}

// UpdateGitxt with infos
func UpdateGitxt(u *Gitxt) error {
return updateGitxt(x, u)
}

// Delete expired
// DeleteExpiredRepositories Delete expired
func DeleteExpiredRepositories() {
if taskStatusTable.IsRunning(_DELETE_EXPIRED_REPOSITORIES) {
if taskStatusTable.IsRunning(deleteExpiredRepositories) {
return
}
taskStatusTable.Start(_DELETE_EXPIRED_REPOSITORIES)
defer taskStatusTable.Stop(_DELETE_EXPIRED_REPOSITORIES)
taskStatusTable.Start(deleteExpiredRepositories)
defer taskStatusTable.Stop(deleteExpiredRepositories)

log.Trace("Doing: DeleteExpiredRepositories")

type GitxtExpired struct {
userId int64
repoId int64
userID int64
repoID int64
hash string
}
expired := []GitxtExpired{}
@@ -211,9 +218,9 @@ func DeleteExpiredRepositories() {
}

for _, tc := range expired {
err := DeleteRepository(tc.userId, tc.repoId)
err := DeleteRepository(tc.userID, tc.repoID)
if err != nil {
log.Warn("Error removing repository %i/%i: %v", tc.userId, tc.repoId, err)
log.Warn("Error removing repository %i/%i: %v", tc.userID, tc.repoID, err)
} else {
log.Trace("Deleted repository %s", tc.hash)
}
@@ -221,13 +228,13 @@ func DeleteExpiredRepositories() {
}
}

// Archive deletion
// DeleteOldRepositoryArchives Archive deletion
func DeleteOldRepositoryArchives() {
if taskStatusTable.IsRunning(_CLEAN_OLD_ARCHIVES) {
if taskStatusTable.IsRunning(cleanOldArchives) {
return
}
taskStatusTable.Start(_CLEAN_OLD_ARCHIVES)
defer taskStatusTable.Stop(_CLEAN_OLD_ARCHIVES)
taskStatusTable.Start(cleanOldArchives)
defer taskStatusTable.Stop(cleanOldArchives)

log.Trace("Doing: DeleteOldRepositoryArchives")

@@ -296,7 +303,7 @@ func removeRepository(path string) {
}
}

// Delete repository :'(
// DeleteRepository Delete repository :'(
func DeleteRepository(ownerID int64, repoID int64) error {
repo := &Gitxt{ID: repoID, UserID: ownerID}
has, err := x.Get(repo)


+ 19
- 9
models/models.go View File

@@ -1,8 +1,11 @@
package models

import (
// msssql
_ "github.com/denisenkom/go-mssqldb"
// mysql
_ "github.com/go-sql-driver/mysql"
// Postgresql
_ "github.com/lib/pq"
"github.com/go-xorm/xorm"
"database/sql"
@@ -33,6 +36,7 @@ type Engine interface {
Where(interface{}, ...interface{}) *xorm.Session
}

// Vars
var (
x *xorm.Engine
tables []interface{}
@@ -47,7 +51,7 @@ var (

func init() {
tables = append(tables,
new(User), new(SshKey), new(Gitxt))
new(User), new(SSHKey), new(Gitxt))

gonicNames := []string{"SSL"}
for _, name := range gonicNames {
@@ -55,6 +59,7 @@ func init() {
}
}

// LoadConfigs to init db
func LoadConfigs() {
sec := setting.Cfg.Section("database")
DbCfg.Type = sec.Key("DB_TYPE").String()
@@ -109,7 +114,7 @@ func parseMSSQLHostPort(info string) (string, string) {

func getEngine() (*xorm.Engine, error) {
connStr := ""
var Param string = "?"
var Param = "?"
if strings.Contains(DbCfg.Name, Param) {
Param = "&"
}
@@ -136,32 +141,34 @@ func getEngine() (*xorm.Engine, error) {
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, DbCfg.Name, DbCfg.User, DbCfg.Passwd)
case "sqlite3":
if !EnableSQLite3 {
return nil, errors.New("This binary version does not build support for SQLite3.")
return nil, errors.New("this binary version does not build support for SQLite3")
}
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
return nil, fmt.Errorf("Fail to create directories: %v", err)
return nil, fmt.Errorf("fail to create directories: %v", err)
}
connStr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
default:
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
return nil, fmt.Errorf("unknown database type: %s", DbCfg.Type)
}
return xorm.NewEngine(DbCfg.Type, connStr)
}

// NewTestEngine to test
func NewTestEngine(x *xorm.Engine) (err error) {
x, err = getEngine()
if err != nil {
return fmt.Errorf("Connect to database: %v", err)
return fmt.Errorf("connect to database: %v", err)
}

x.SetMapper(core.GonicMapper{})
return x.StoreEngine("InnoDB").Sync2(tables...)
}

// SetEngine to use
func SetEngine() (err error) {
x, err = getEngine()
if err != nil {
return fmt.Errorf("Fail to connect to database: %v", err)
return fmt.Errorf("fail to connect to database: %v", err)
}

x.SetMapper(core.GonicMapper{})
@@ -177,7 +184,7 @@ func SetEngine() (err error) {
MaxDays: sec.Key("MAX_DAYS").MustInt64(3),
})
if err != nil {
return fmt.Errorf("Fail to create 'xorm.log': %v", err)
return fmt.Errorf("fail to create 'xorm.log': %v", err)
}

x.SetLogger(xorm.NewSimpleLogger3(logger, xorm.DEFAULT_LOG_PREFIX, xorm.DEFAULT_LOG_FLAG, core.LOG_DEBUG))
@@ -185,6 +192,7 @@ func SetEngine() (err error) {
return nil
}

// NewEngine to use
func NewEngine() (err error) {
if err = SetEngine(); err != nil {
return err
@@ -193,16 +201,18 @@ func NewEngine() (err error) {
// TODO: here do migrations if any

if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
return fmt.Errorf("sync database struct error: %v\n", err)
return fmt.Errorf("sync database struct error: %v", err)
}

return nil
}

// Ping pong
func Ping() error {
return x.Ping()
}

// InitDb from config
func InitDb() {
LoadConfigs()



+ 9
- 6
models/sshkey.go View File

@@ -4,7 +4,8 @@ import (
"time"
)

type SshKey struct {
// SSHKey struct
type SSHKey struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"INDEX NOT NULL"`

@@ -23,11 +24,13 @@ type SshKey struct {
// UserID
}

func (ssh_key *SshKey) BeforeInsert() {
ssh_key.CreatedUnix = time.Now().Unix()
ssh_key.UpdatedUnix = ssh_key.CreatedUnix
// BeforeInsert hooks
func (sshKey *SSHKey) BeforeInsert() {
sshKey.CreatedUnix = time.Now().Unix()
sshKey.UpdatedUnix = sshKey.CreatedUnix
}

func (ssh_key *SshKey) BeforeUpdate() {
ssh_key.UpdatedUnix = time.Now().Unix()
// BeforeUpdate hooks
func (sshKey *SSHKey) BeforeUpdate() {
sshKey.UpdatedUnix = time.Now().Unix()
}

+ 38
- 27
models/user.go View File

@@ -19,6 +19,7 @@ import (
"dev.sigpipe.me/dashie/git.txt/stuff/mailer"
)

// User struct
type User struct {
ID int64 `xorm:"pk autoincr"`
UserName string `xorm:"UNIQUE NOT NULL"`
@@ -43,11 +44,13 @@ type User struct {
// SshKeys
}

// BeforeInsert hooks
func (user *User) BeforeInsert() {
user.CreatedUnix = time.Now().Unix()
user.UpdatedUnix = user.CreatedUnix
}

// BeforeUpdate hooks
func (user *User) BeforeUpdate() {
user.UpdatedUnix = time.Now().Unix()
}
@@ -153,21 +156,22 @@ func isUsableName(names, patterns []string, name string) error {
return nil
}

// IsUsableUsername or not
func IsUsableUsername(name string) error {
return isUsableName(reservedUsernames, reservedUserPatterns, name)
}

// EncodePasswd encodes password to safe format.
func (u *User) EncodePasswd() {
newPasswd := pbkdf2.Key([]byte(u.Password), []byte(u.Salt), 10000, 50, sha256.New)
u.Password = fmt.Sprintf("%x", newPasswd)
func (user *User) EncodePasswd() {
newPasswd := pbkdf2.Key([]byte(user.Password), []byte(user.Salt), 10000, 50, sha256.New)
user.Password = fmt.Sprintf("%x", newPasswd)
}

// ValidatePassword checks if given password matches the one belongs to the user.
func (u *User) ValidatePassword(passwd string) bool {
newUser := &User{Password: passwd, Salt: u.Salt}
func (user *User) ValidatePassword(passwd string) bool {
newUser := &User{Password: passwd, Salt: user.Salt}
newUser.EncodePasswd()
return subtle.ConstantTimeCompare([]byte(u.Password), []byte(newUser.Password)) == 1
return subtle.ConstantTimeCompare([]byte(user.Password), []byte(newUser.Password)) == 1
}

// GetUserSalt returns a ramdom user salt token.
@@ -180,7 +184,7 @@ func UserPath(userName string) string {
return filepath.Join(setting.RepositoryRoot, strings.ToLower(userName))
}

// Create a new user and do some validation
// CreateUser and do some validation
func CreateUser(u *User) (err error) {
if err = IsUsableUsername(u.UserName); err != nil {
return err
@@ -227,11 +231,12 @@ func updateUser(e Engine, u *User) error {
return err
}

// UpdateUser with datas
func UpdateUser(u *User) error {
return updateUser(x, u)
}

// Login validates user name and password.
// UserLogin validates user name and password.
func UserLogin(username, password string) (*User, error) {
var user *User
if strings.Contains(username, "@") {
@@ -258,12 +263,12 @@ func UserLogin(username, password string) (*User, error) {

// get user by verify code
func getVerifyUser(code string) (user *User) {
if len(code) <= tool.TIME_LIMIT_CODE_LENGTH {
if len(code) <= tool.TimeLimitCodeLength {
return nil
}

// use tail hex username query user
hexStr := code[tool.TIME_LIMIT_CODE_LENGTH:]
hexStr := code[tool.TimeLimitCodeLength:]
if b, err := hex.DecodeString(hexStr); err == nil {
if user, err = GetUserByName(string(b)); user != nil {
return user
@@ -275,14 +280,14 @@ func getVerifyUser(code string) (user *User) {
return nil
}

// verify active code when active account
// VerifyUserActiveCode when active account
func VerifyUserActiveCode(code string) (user *User) {
// HARDCODED
minutes := 180

if user = getVerifyUser(code); user != nil {
// time limit code
prefix := code[:tool.TIME_LIMIT_CODE_LENGTH]
prefix := code[:tool.TimeLimitCodeLength]
data := com.ToStr(user.ID) + user.Email + user.LowerName + user.Password + user.Rands

if tool.VerifyTimeLimitCode(data, minutes, prefix) {
@@ -293,18 +298,18 @@ func VerifyUserActiveCode(code string) (user *User) {
}

// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
func (u *User) GenerateEmailActivateCode(email string) string {
func (user *User) GenerateEmailActivateCode(email string) string {
code := tool.CreateTimeLimitCode(
com.ToStr(u.ID)+email+u.LowerName+u.Password+u.Rands,180, nil)
com.ToStr(user.ID)+email+user.LowerName+user.Password+user.Rands,180, nil)

// Add tail hex username
code += hex.EncodeToString([]byte(u.LowerName))
code += hex.EncodeToString([]byte(user.LowerName))
return code
}

// GenerateActivateCode generates an activate code based on user information.
func (u *User) GenerateActivateCode() string {
return u.GenerateEmailActivateCode(u.Email)
func (user *User) GenerateActivateCode() string {
return user.GenerateEmailActivateCode(user.Email)
}

// mailerUser is a wrapper for satisfying mailer.User interface.
@@ -312,26 +317,32 @@ type mailerUser struct {
user *User
}

func (this mailerUser) ID() int64 {
return this.user.ID
// ID id
func (mUser mailerUser) ID() int64 {
return mUser.user.ID
}

func (this mailerUser) Email() string {
return this.user.Email
// Email func
func (mUser mailerUser) Email() string {
return mUser.user.Email
}

func (this mailerUser) DisplayName() string {
return this.user.UserName
// DisplayName func
func (mUser mailerUser) DisplayName() string {
return mUser.user.UserName
}

func (this mailerUser) GenerateActivateCode() string {
return this.user.GenerateActivateCode()
// GenerateActivateCode func
func (mUser mailerUser) GenerateActivateCode() string {
return mUser.user.GenerateActivateCode()
}

func (this mailerUser) GenerateEmailActivateCode(email string) string {
return this.user.GenerateEmailActivateCode(email)
// GenerateEmailActivateCode func
func (mUser mailerUser) GenerateEmailActivateCode(email string) string {
return mUser.user.GenerateEmailActivateCode(email)
}

// NewMailerUser mail user
func NewMailerUser(u *User) mailer.User {
return mailerUser{u}
}

+ 3
- 2
routers/admin/admin.go View File

@@ -10,7 +10,7 @@ import (
)

const (
DASHBOARD = "admin/dashboard"
tmplDashboard = "admin/dashboard"
)

var (
@@ -96,6 +96,7 @@ func updateSystemStatus() {
sysStatus.NumGC = m.NumGC
}

// Dashboard GET
func Dashboard(ctx *context.Context) {
ctx.Title("admin.dashboard.title")
ctx.PageIs("AdminDashboard")
@@ -105,5 +106,5 @@ func Dashboard(ctx *context.Context) {
updateSystemStatus()
ctx.Data["SysStatus"] = sysStatus
ctx.Data["Entries"] = cron.ListTasks()
ctx.HTML(200, DASHBOARD)
ctx.HTML(200, tmplDashboard)
}

+ 35
- 30
routers/gitxt/gitxt.go View File

@@ -23,12 +23,13 @@ import (
)

const (
NEW = "gitxt/new"
VIEW = "gitxt/view"
LIST = "gitxt/list"
EDIT = "gitxt/edit"
tmplNew = "gitxt/new"
tmplView = "gitxt/view"
tmplList = "gitxt/list"
tmplEdit = "gitxt/edit"
)

// New GET
func New(ctx *context.Context) {
ctx.Title("gitxt_new.title")
ctx.PageIs("GitxtNew")
@@ -40,9 +41,10 @@ func New(ctx *context.Context) {
// Initial expiry
ctx.Data["ExpiryHours"] = 0

ctx.Success(NEW)
ctx.Success(tmplNew)
}

// NewPost POST
func NewPost(ctx *context.Context, f form.Gitxt) {
// Reject-redirect if not logged-in and anonymous create is deactivated
if !setting.AnonymousCreate && !ctx.IsLogged {
@@ -55,7 +57,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {

for i := range f.FilesFilename {
// For each filename sanitize it
f.FilesFilename[i] = sanitize.SanitizeFilename(f.FilesFilename[i])
f.FilesFilename[i] = sanitize.Filename(f.FilesFilename[i])
if len(f.FilesFilename[i]) == 0 || f.FilesFilename[i] == "." {
// If length is zero, use default filename
f.FilesFilename[i] = fmt.Sprintf("gitxt%d.txt", i)
@@ -81,7 +83,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {

// We got an error in the manual validation step, render with error
if ctx.HasError() {
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}

@@ -113,7 +115,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {

log.Trace("Repository deleted: %s for %s", repositoryName, repositoryUser)

ctx.Success(NEW)
ctx.Success(tmplNew)
return
}

@@ -130,7 +132,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {
log.Warn("init_error_create_blob: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_create_blob")
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}
blobs = append(blobs, blob)
@@ -142,7 +144,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {
log.Warn("init_error_get_index: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_get_index")
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}

@@ -158,7 +160,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {
log.Warn("init_error_add_entry: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_add_entry")
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}
}
@@ -170,7 +172,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {
log.Warn("init_error_index_write_tree: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_index_write_tree")
ctx.Success(NEW)
ctx.Success(tmplNew)
return

}
@@ -181,7 +183,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {
log.Warn("init_error_lookup_tree: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_lookup_tree")
ctx.Success(NEW)
ctx.Success(tmplNew)
return

}
@@ -196,7 +198,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {
log.Warn("init_error_commit: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_commit")
ctx.Success(NEW)
ctx.Success(tmplNew)
return

}
@@ -224,7 +226,7 @@ func NewPost(ctx *context.Context, f form.Gitxt) {
switch {
case models.IsErrHashAlreadyExist(err):
ctx.Data["Err_Hash"] = true
ctx.RenderWithErr(ctx.Tr("gitxt_new.hash_been_taken"), NEW, &f)
ctx.RenderWithErr(ctx.Tr("gitxt_new.hash_been_taken"), tmplNew, &f)
default:
ctx.Handle(500, "NewPost", err)
}
@@ -310,10 +312,10 @@ func View(ctx *context.Context) {
ctx.Data["IsOwner"] = false
}

ctx.Success(VIEW)
ctx.Success(tmplView)
}

// RawFile GET
func RawFile(ctx *context.Context) {
file := ctx.Params("path")

@@ -356,7 +358,7 @@ func RawFile(ctx *context.Context) {
}
}

// List uploads, manage auth'ed user or not and from /:user too
// ListUploads manage auth'ed user or not and from /:user too
func ListUploads(ctx *context.Context) {
ctx.Title("gitxt_list.title")
ctx.PageIs("GitxtList")
@@ -409,9 +411,10 @@ func ListUploads(ctx *context.Context) {
ctx.Data["Total"] = gitxtsCount
ctx.Data["Page"] = paginater.New(int(gitxtsCount), opts.PageSize, page, 5)

ctx.Success(LIST)
ctx.Success(tmplList)
}

// DeletePost GET
func DeletePost(ctx *context.Context, f form.GitxtDelete) {
if ctx.HasError() {
ctx.JSONSuccess(map[string]interface{}{
@@ -446,6 +449,7 @@ func DeletePost(ctx *context.Context, f form.GitxtDelete) {
return
}

// Edit git.txt
func Edit(ctx *context.Context) {
ctx.Title("gitxt_edit.title")
ctx.PageIs("GitxtEdit")
@@ -514,9 +518,10 @@ func Edit(ctx *context.Context) {
ctx.Data["FilesFilename"] = FilesFilename
ctx.Data["FilesNotHandled"] = FilesNotHandled

ctx.Success(EDIT)
ctx.Success(tmplEdit)
}

// EditPost POST
func EditPost(ctx *context.Context, f form.GitxtEdit) {
if !ctx.IsLogged {
ctx.Redirect(setting.AppSubURL + "/")
@@ -534,7 +539,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {

for i := range f.FilesFilename {
// For each filename sanitize it
f.FilesFilename[i] = sanitize.SanitizeFilename(f.FilesFilename[i])
f.FilesFilename[i] = sanitize.Filename(f.FilesFilename[i])
if len(f.FilesFilename[i]) == 0 || f.FilesFilename[i] == "." {
// If length is zero, use default filename
f.FilesFilename[i] = fmt.Sprintf("gitxt%d.txt", i)
@@ -561,7 +566,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {

// We got an error in the manual validation step, render with error
if ctx.HasError() {
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}

@@ -604,7 +609,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {
log.Warn("init_error_create_blob: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_create_blob")
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}
}
@@ -617,7 +622,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {
log.Warn("init_error_get_index: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_get_index")
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}

@@ -635,7 +640,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {
log.Warn("init_error_add_entry: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_add_entry")
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}
}
@@ -647,7 +652,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {
log.Warn("init_error_index_write_tree: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_index_write_tree")
ctx.Success(NEW)
ctx.Success(tmplNew)
return

}
@@ -658,7 +663,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {
log.Warn("init_error_lookup_tree: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_lookup_tree")
ctx.Success(NEW)
ctx.Success(tmplNew)
return

}
@@ -669,7 +674,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {
log.Warn("git_error_get_head: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_get_head")
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}

@@ -679,7 +684,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {
log.Warn("git_error_get_head_commit: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_get_head_commit")
ctx.Success(NEW)
ctx.Success(tmplNew)
return
}

@@ -693,7 +698,7 @@ func EditPost(ctx *context.Context, f form.GitxtEdit) {
log.Warn("init_error_commit: %s", err)
ctx.Data["HasError"] = true
ctx.Data["ErrorMsg"] = ctx.Tr("gitxt_git.error_commit")
ctx.Success(NEW)
ctx.Success(tmplNew)
return

}


+ 1
- 0
routers/hub.go View File

@@ -2,6 +2,7 @@ package routers

import "dev.sigpipe.me/dashie/git.txt/context"

// NotFound 404
func NotFound(ctx *context.Context) {
ctx.Title(ctx.Tr("error.page_not_found"))
ctx.Handle(404, "home.NotFound", nil)

+ 1
- 0
routers/repo/archive.go View File

@@ -14,6 +14,7 @@ import (
"dev.sigpipe.me/dashie/git.txt/stuff/gite"
)

// DownloadArchive of repository
func DownloadArchive(ctx *context.Context) {
var (
uri = ctx.Params("*")


+ 22
- 17
routers/repo/repo.go View File

@@ -26,16 +26,17 @@ import (
)

const (
ENV_AUTH_USER_ID = "GITXT_AUTH_USER_ID"
ENV_AUTH_USER_NAME = "GITXT_AUTH_USER_NAME"
ENV_AUTH_USER_EMAIL = "GITXT_AUTH_USER_EMAIL"
ENV_REPO_OWNER_NAME = "GITXT_REPO_OWNER_NAME"
ENV_REPO_OWNER_SALT_MD5 = "GITXT_REPO_OWNER_SALT_MD5"
ENV_REPO_ID = "GITXT_REPO_ID"
ENV_REPO_NAME = "GITXT_REPO_NAME"
ENV_REPO_CUSTOM_HOOKS_PATH = "GITXT_REPO_CUSTOM_HOOKS_PATH"
envAuthUserID = "GITXT_AUTH_USER_ID"
envAuthUserName = "GITXT_AUTH_USER_NAME"
envAuthUserEmail = "GITXT_AUTH_USER_EMAIL"
envRepoOwnerName = "GITXT_REPO_OWNER_NAME"
envRepoOwnerSaltMd5 = "GITXT_REPO_OWNER_SALT_MD5"
envRepoID = "GITXT_REPO_ID"
envRepoName = "GITXT_REPO_NAME"
envRepoCustomHooksPath = "GITXT_REPO_CUSTOM_HOOKS_PATH"
)

// HTTPContext struct
type HTTPContext struct {
*context.Context
OwnerName string
@@ -51,6 +52,7 @@ func askCredentials(ctx *context.Context, status int, text string) {
ctx.HandleText(status, text)
}

// HTTPContexter magic
func HTTPContexter() macaron.Handler {
return func(ctx *context.Context) {
ownerName := ctx.Params(":user")
@@ -181,6 +183,7 @@ func (h *serviceHandler) sendFile(contentType string) {
http.ServeFile(h.w, h.r, reqFile)
}

// ComposeHookEnvsOptions struct
type ComposeHookEnvsOptions struct {
AuthUser *models.User
OwnerName string
@@ -190,17 +193,18 @@ type ComposeHookEnvsOptions struct {
RepoPath string
}

// ComposeHookEnvs magic
func ComposeHookEnvs(opts ComposeHookEnvsOptions) []string {
envs := []string{
"SSH_ORIGINAL_COMMAND=1",
ENV_AUTH_USER_ID + "=" + com.ToStr(opts.AuthUser.ID),
ENV_AUTH_USER_NAME + "=" + opts.AuthUser.UserName,
ENV_AUTH_USER_EMAIL + "=" + opts.AuthUser.Email,
ENV_REPO_OWNER_NAME + "=" + opts.OwnerName,
ENV_REPO_OWNER_SALT_MD5 + "=" + tool.MD5(opts.OwnerSalt),
ENV_REPO_ID + "=" + com.ToStr(opts.RepoID),
ENV_REPO_NAME + "=" + opts.RepoName,
ENV_REPO_CUSTOM_HOOKS_PATH + "=" + path.Join(opts.RepoPath, "custom_hooks"),
envAuthUserID + "=" + com.ToStr(opts.AuthUser.ID),
envAuthUserName + "=" + opts.AuthUser.UserName,
envAuthUserEmail + "=" + opts.AuthUser.Email,
envRepoOwnerName + "=" + opts.OwnerName,
envRepoOwnerSaltMd5 + "=" + tool.MD5(opts.OwnerSalt),
envRepoID + "=" + com.ToStr(opts.RepoID),
envRepoName + "=" + opts.RepoName,
envRepoCustomHooksPath + "=" + path.Join(opts.RepoPath, "custom_hooks"),
}
return envs
}
@@ -364,6 +368,7 @@ func getGitRepoPath(repoDir string) (string, error) {
return filename, nil
}

// HTTP Context
func HTTP(ctx *HTTPContext) {
for _, route := range routes {
reqPath := strings.ToLower(ctx.Req.URL.Path)
@@ -375,7 +380,7 @@ func HTTP(ctx *HTTPContext) {
// We perform check here because routes matched in cmd/web.go is wider than needed,
// but we only want to output this message only if user is really trying to access
// Git HTTP endpoints.
if setting.DisableHttpGit {
if setting.DisableHTTPGit {
ctx.HandleText(http.StatusForbidden, "Interacting with repositories by HTTP protocol is not disabled")
return
}


+ 32
- 27
routers/user/auth.go View File

@@ -13,10 +13,10 @@ import (
)

const (
LOGIN = "user/auth/login"
REGISTER = "user/auth/register"
FORGOT_PASSWORD = "user/auth/forgot_password"
RESET_PASSWORD = "user/auth/reset_password"
tmplLogin = "user/auth/login"
tmplRegister = "user/auth/register"
tmplForgotPassword = "user/auth/forgot_password"
tmplResetPassword = "user/auth/reset_password"
)

// isValidRedirect returns false if the URL does not redirect to same site.
@@ -69,7 +69,7 @@ func AutoLogin(c *context.Context) (bool, error) {
return true, nil
}

// Login
// Login GET
func Login(ctx *context.Context) {
ctx.Title("login.title")

@@ -97,21 +97,22 @@ func Login(ctx *context.Context) {
return
}

ctx.HTML(200, LOGIN)
ctx.HTML(200, tmplLogin)
}

// LoginPost POST
func LoginPost(ctx *context.Context, f form.Login) {
ctx.Title("login.title")

if ctx.HasError() {
ctx.Success(LOGIN)
ctx.Success(tmplLogin)
return
}

u, err := models.UserLogin(f.UserName, f.Password)
if err != nil {
if errors.IsUserNotExist(err) {
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), LOGIN, &f)
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tmplLogin, &f)
} else {
ctx.ServerError("UserSignIn", err)
}
@@ -149,7 +150,7 @@ func afterLogin(ctx *context.Context, u *models.User, remember bool) {
ctx.Redirect(setting.AppSubURL + "/")
}

// Registration
// Register GET
func Register(ctx *context.Context) {
ctx.Title("register.title")
if ! setting.CanRegister {
@@ -158,9 +159,10 @@ func Register(ctx *context.Context) {
return
}

ctx.HTML(200, REGISTER)
ctx.HTML(200, tmplRegister)
}

// RegisterPost POST
func RegisterPost(ctx *context.Context, f form.Register) {
ctx.Title("register.title")

@@ -171,14 +173,14 @@ func RegisterPost(ctx *context.Context, f form.Register) {
}

if ctx.HasError() {
ctx.HTML(200, REGISTER)
ctx.HTML(200, tmplRegister)
return
}

if f.Password != f.Repeat {
ctx.Data["Err_Password"] = true
ctx.Data["Err_Retype"] = true
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), REGISTER, &f)
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tmplRegister, &f)
return
}

@@ -192,13 +194,13 @@ func RegisterPost(ctx *context.Context, f form.Register) {
switch {
case models.IsErrUserAlreadyExist(err):
ctx.Data["Err_UserName"] = true
ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), REGISTER, &f)
ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tmplRegister, &f)
case models.IsErrNameReserved(err):
ctx.Data["Err_UserName"] = true
ctx.RenderWithErr(ctx.Tr("form.username_reserved"), REGISTER, &f)
ctx.RenderWithErr(ctx.Tr("form.username_reserved"), tmplRegister, &f)
case models.IsErrNamePatternNotAllowed(err):
ctx.Data["Err_UserName"] = true
ctx.RenderWithErr(ctx.Tr("form.username_pattern_not_allowed"), REGISTER, &f)
ctx.RenderWithErr(ctx.Tr("form.username_pattern_not_allowed"), tmplRegister, &f)
default:
ctx.Handle(500, "CreateUser", err)
}
@@ -222,7 +224,7 @@ func RegisterPost(ctx *context.Context, f form.Register) {
ctx.Redirect(setting.AppSubURL + "/user/login")
}

// Logout
// Logout GET
func Logout(ctx *context.Context) {
ctx.Session.Delete("uid")
ctx.Session.Delete("uname")
@@ -233,7 +235,7 @@ func Logout(ctx *context.Context) {
}


// ResetPassword
// ResetPasswd GET
func ResetPasswd(ctx *context.Context) {
ctx.Title("auth.reset_password")
code := ctx.Query("code")
@@ -243,9 +245,10 @@ func ResetPasswd(ctx *context.Context) {
}
ctx.Data["Code"] = code
ctx.Data["IsResetForm"] = true
ctx.HTML(200, RESET_PASSWORD)
ctx.HTML(200, tmplResetPassword)
}

// ResetPasswdPost POST
func ResetPasswdPost(ctx *context.Context) {
ctx.Title("auth.reset_password")

@@ -262,7 +265,7 @@ func ResetPasswdPost(ctx *context.Context) {
if len(passwd) < 6 {
ctx.Data["IsResetForm"] = true
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_too_short"), RESET_PASSWORD, nil)
ctx.RenderWithErr(ctx.Tr("auth.password_too_short"), tmplResetPassword, nil)
return
}

@@ -287,23 +290,25 @@ func ResetPasswdPost(ctx *context.Context) {
return
}
ctx.Data["IsResetFailed"] = true
ctx.HTML(200, RESET_PASSWORD)
ctx.HTML(200, tmplResetPassword)
}

// ForgotPasswd GET
func ForgotPasswd(ctx *context.Context) {
ctx.Title("auth.forgot_password")

if setting.MailService == nil {
ctx.Data["IsResetDisable"] = true
ctx.HTML(200, FORGOT_PASSWORD)
ctx.HTML(200, tmplForgotPassword)
return
}

ctx.Data["IsResetRequest"] = true

ctx.HTML(200, FORGOT_PASSWORD)
ctx.HTML(200, tmplForgotPassword)
}

// ForgotPasswdPost POST
func ForgotPasswdPost(ctx *context.Context) {
ctx.Title("auth.forgot_password")

@@ -323,18 +328,18 @@ func ForgotPasswdPost(ctx *context.Context) {
ctx.Data["Hours"] = 180 / 60
ctx.Data["IsResetSent"] = true
log.Trace("User doesn't exists")
ctx.HTML(200, FORGOT_PASSWORD)
ctx.HTML(200, tmplForgotPassword)
return
} else {
ctx.Handle(500, "user.ResetPasswd(check existence)", err)
}

ctx.Handle(500, "user.ResetPasswd(check existence)", err)
return
}

if ctx.Cache.IsExist("MailResendLimit_" + u.LowerName) {
log.Trace("Mail Resend limited")
ctx.Data["ResendLimited"] = true
ctx.HTML(200, FORGOT_PASSWORD)
ctx.HTML(200, tmplForgotPassword)
return
}

@@ -346,5 +351,5 @@ func ForgotPasswdPost(ctx *context.Context) {
// HARDCODED
ctx.Data["Hours"] = 180 / 60
ctx.Data["IsResetSent"] = true
ctx.HTML(200, FORGOT_PASSWORD)
ctx.HTML(200, tmplForgotPassword)
}

+ 5
- 3
routers/user/setting.go View File

@@ -7,23 +7,25 @@ import (
)

const (
SETTINGS_PROFILE = "user/settings/profile"
tmplSettingsProfile = "user/settings/profile"
)

// Settings GET
func Settings(ctx *context.Context) {
ctx.Title("settings.title")
ctx.PageIs("SettingsProfile")
ctx.Data["email"] = ctx.User.Email
ctx.Success(SETTINGS_PROFILE)
ctx.Success(tmplSettingsProfile)
}

// SettingsPost POST
func SettingsPost(ctx *context.Context, f form.UpdateSettingsProfile) {
ctx.Title("settings.title")
ctx.PageIs("SettingsProfile")
ctx.Data["origin_name"] = ctx.User.UserName

if ctx.HasError() {
ctx.Success(SETTINGS_PROFILE)
ctx.Success(tmplSettingsProfile)
return
}



+ 31
- 16
setting/setting.go View File

@@ -17,21 +17,26 @@ import (
"net/mail"
)

// Scheme type
type Scheme string

// Schemes
const (
SCHEME_HTTP Scheme = "http"
SCHEME_HTTPS Scheme = "https"
SCHEME_FCGI Scheme = "fcgi"
SCHEME_UNIX_SOCKET Scheme = "unix"
SchemeHTTP Scheme = "http"
SchemeHTTPS Scheme = "https"
SchemeFCGI Scheme = "fcgi"
SchemeUnixSocket Scheme = "unix"
)


// Settings
var (
// Build infos added by -ldflags
BuildTime string
BuildGitHash string

// App Settings

AppVer string
AppPath string
AppName string
@@ -53,6 +58,7 @@ var (
}

// Server settings

Protocol Scheme
UnixSocketPermission uint32
Domain string
@@ -66,12 +72,14 @@ var (
}

// Database Settings

UseSQLite3 bool
UseMySQL bool
UsePostgreSQL bool
UseMSSQL bool

// Global setting objects

CustomConf string
IsWindows bool
Cfg *ini.File
@@ -79,20 +87,24 @@ var (
RobotsTxtPath string

// Log settings

LogRootPath string
LogModes []string
LogConfigs []interface{}

// Repository settings
RepositoryRoot string
DisableHttpGit bool
GitBinary string

RepositoryRoot string
DisableHTTPGit bool
GitBinary string

// Session settings

SessionConfig session.Options
CSRFCookieName string

// Security settings

InstallLock bool
SecretKey string
LoginRememberDays int
@@ -103,11 +115,12 @@ var (
LoginStatusCookieName string

// Cache settings

CacheAdapter string
CacheInterval int
CacheConn string

// I18n settings
// Langs settings
Langs []string
Names []string
dateLangs map[string]string
@@ -128,7 +141,7 @@ var (
AngledQuotes bool
}

// Static settings
// Bloby struct for static limitations
Bloby struct {
MaxSizeDisplay int64
MaxPageDisplay int64
@@ -188,6 +201,7 @@ func forcePathSeparator(path string) {
}
}

// InitConfig from file
func InitConfig() {
workDir, err := WorkDir()
if err != nil {
@@ -221,27 +235,27 @@ func InitConfig() {
}

// Check if has app suburl.
appUrl, err := url.Parse(AppURL)
appURL, err := url.Parse(AppURL)
if err != nil {
log.Fatal(2, "Invalid ROOT_URL '%s': %s", AppURL, err)
}
// Suburl should start with '/' and end without '/', such as '/{subpath}'.
// This value is empty if site does not have sub-url.
AppSubURL = strings.TrimSuffix(appUrl.Path, "/")
AppSubURL = strings.TrimSuffix(appURL.Path, "/")
AppSubURLDepth = strings.Count(AppSubURL, "/")