mirror of
https://github.com/gogs/gogs.git
synced 2026-02-07 06:59:28 +01:00
internal: move packages under this directory (#5836)
* Rename pkg -> internal * Rename routes -> route * Move route -> internal/route * Rename models -> db * Move db -> internal/db * Fix route2 -> route * Move cmd -> internal/cmd * Bump version
This commit is contained in:
183
cmd/admin.go
183
cmd/admin.go
@@ -1,183 +0,0 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"gogs.io/gogs/models"
|
||||
"gogs.io/gogs/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
Admin = cli.Command{
|
||||
Name: "admin",
|
||||
Usage: "Perform admin operations on command line",
|
||||
Description: `Allow using internal logic of Gogs without hacking into the source code
|
||||
to make automatic initialization process more smoothly`,
|
||||
Subcommands: []cli.Command{
|
||||
subcmdCreateUser,
|
||||
subcmdDeleteInactivateUsers,
|
||||
subcmdDeleteRepositoryArchives,
|
||||
subcmdDeleteMissingRepositories,
|
||||
subcmdGitGcRepos,
|
||||
subcmdRewriteAuthorizedKeys,
|
||||
subcmdSyncRepositoryHooks,
|
||||
subcmdReinitMissingRepositories,
|
||||
},
|
||||
}
|
||||
|
||||
subcmdCreateUser = cli.Command{
|
||||
Name: "create-user",
|
||||
Usage: "Create a new user in database",
|
||||
Action: runCreateUser,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("name", "", "Username"),
|
||||
stringFlag("password", "", "User password"),
|
||||
stringFlag("email", "", "User email address"),
|
||||
boolFlag("admin", "User is an admin"),
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
|
||||
subcmdDeleteInactivateUsers = cli.Command{
|
||||
Name: "delete-inactive-users",
|
||||
Usage: "Delete all inactive accounts",
|
||||
Action: adminDashboardOperation(
|
||||
models.DeleteInactivateUsers,
|
||||
"All inactivate accounts have been deleted successfully",
|
||||
),
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
|
||||
subcmdDeleteRepositoryArchives = cli.Command{
|
||||
Name: "delete-repository-archives",
|
||||
Usage: "Delete all repositories archives",
|
||||
Action: adminDashboardOperation(
|
||||
models.DeleteRepositoryArchives,
|
||||
"All repositories archives have been deleted successfully",
|
||||
),
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
|
||||
subcmdDeleteMissingRepositories = cli.Command{
|
||||
Name: "delete-missing-repositories",
|
||||
Usage: "Delete all repository records that lost Git files",
|
||||
Action: adminDashboardOperation(
|
||||
models.DeleteMissingRepositories,
|
||||
"All repositories archives have been deleted successfully",
|
||||
),
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
|
||||
subcmdGitGcRepos = cli.Command{
|
||||
Name: "collect-garbage",
|
||||
Usage: "Do garbage collection on repositories",
|
||||
Action: adminDashboardOperation(
|
||||
models.GitGcRepos,
|
||||
"All repositories have done garbage collection successfully",
|
||||
),
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
|
||||
subcmdRewriteAuthorizedKeys = cli.Command{
|
||||
Name: "rewrite-authorized-keys",
|
||||
Usage: "Rewrite '.ssh/authorized_keys' file (caution: non-Gogs keys will be lost)",
|
||||
Action: adminDashboardOperation(
|
||||
models.RewriteAuthorizedKeys,
|
||||
"All public keys have been rewritten successfully",
|
||||
),
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
|
||||
subcmdSyncRepositoryHooks = cli.Command{
|
||||
Name: "resync-hooks",
|
||||
Usage: "Resync pre-receive, update and post-receive hooks",
|
||||
Action: adminDashboardOperation(
|
||||
models.SyncRepositoryHooks,
|
||||
"All repositories' pre-receive, update and post-receive hooks have been resynced successfully",
|
||||
),
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
|
||||
subcmdReinitMissingRepositories = cli.Command{
|
||||
Name: "reinit-missing-repositories",
|
||||
Usage: "Reinitialize all repository records that lost Git files",
|
||||
Action: adminDashboardOperation(
|
||||
models.ReinitMissingRepositories,
|
||||
"All repository records that lost Git files have been reinitialized successfully",
|
||||
),
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runCreateUser(c *cli.Context) error {
|
||||
if !c.IsSet("name") {
|
||||
return fmt.Errorf("Username is not specified")
|
||||
} else if !c.IsSet("password") {
|
||||
return fmt.Errorf("Password is not specified")
|
||||
} else if !c.IsSet("email") {
|
||||
return fmt.Errorf("Email is not specified")
|
||||
}
|
||||
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
setting.NewContext()
|
||||
models.LoadConfigs()
|
||||
models.SetEngine()
|
||||
|
||||
if err := models.CreateUser(&models.User{
|
||||
Name: c.String("name"),
|
||||
Email: c.String("email"),
|
||||
Passwd: c.String("password"),
|
||||
IsActive: true,
|
||||
IsAdmin: c.Bool("admin"),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CreateUser: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("New user '%s' has been successfully created!\n", c.String("name"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func adminDashboardOperation(operation func() error, successMessage string) func(*cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
setting.NewContext()
|
||||
models.LoadConfigs()
|
||||
models.SetEngine()
|
||||
|
||||
if err := operation(); err != nil {
|
||||
functionName := runtime.FuncForPC(reflect.ValueOf(operation).Pointer()).Name()
|
||||
return fmt.Errorf("%s: %v", functionName, err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", successMessage)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
137
cmd/backup.go
137
cmd/backup.go
@@ -1,137 +0,0 @@
|
||||
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/unknwon/cae/zip"
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
log "gopkg.in/clog.v1"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"gogs.io/gogs/models"
|
||||
"gogs.io/gogs/pkg/setting"
|
||||
)
|
||||
|
||||
var Backup = cli.Command{
|
||||
Name: "backup",
|
||||
Usage: "Backup files and database",
|
||||
Description: `Backup dumps and compresses all related files and database into zip file,
|
||||
which can be used for migrating Gogs to another server. The output format is meant to be
|
||||
portable among all supported database engines.`,
|
||||
Action: runBackup,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
boolFlag("verbose, v", "Show process details"),
|
||||
stringFlag("tempdir, t", os.TempDir(), "Temporary directory path"),
|
||||
stringFlag("target", "./", "Target directory path to save backup archive"),
|
||||
stringFlag("archive-name", fmt.Sprintf("gogs-backup-%s.zip", time.Now().Format("20060102150405")), "Name of backup archive"),
|
||||
boolFlag("database-only", "Only dump database"),
|
||||
boolFlag("exclude-repos", "Exclude repositories"),
|
||||
},
|
||||
}
|
||||
|
||||
const _CURRENT_BACKUP_FORMAT_VERSION = 1
|
||||
const _ARCHIVE_ROOT_DIR = "gogs-backup"
|
||||
|
||||
func runBackup(c *cli.Context) error {
|
||||
zip.Verbose = c.Bool("verbose")
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
setting.NewContext()
|
||||
models.LoadConfigs()
|
||||
models.SetEngine()
|
||||
|
||||
tmpDir := c.String("tempdir")
|
||||
if !com.IsExist(tmpDir) {
|
||||
log.Fatal(0, "'--tempdir' does not exist: %s", tmpDir)
|
||||
}
|
||||
rootDir, err := ioutil.TempDir(tmpDir, "gogs-backup-")
|
||||
if err != nil {
|
||||
log.Fatal(0, "Fail to create backup root directory '%s': %v", rootDir, err)
|
||||
}
|
||||
log.Info("Backup root directory: %s", rootDir)
|
||||
|
||||
// Metadata
|
||||
metaFile := path.Join(rootDir, "metadata.ini")
|
||||
metadata := ini.Empty()
|
||||
metadata.Section("").Key("VERSION").SetValue(com.ToStr(_CURRENT_BACKUP_FORMAT_VERSION))
|
||||
metadata.Section("").Key("DATE_TIME").SetValue(time.Now().String())
|
||||
metadata.Section("").Key("GOGS_VERSION").SetValue(setting.AppVer)
|
||||
if err = metadata.SaveTo(metaFile); err != nil {
|
||||
log.Fatal(0, "Fail to save metadata '%s': %v", metaFile, err)
|
||||
}
|
||||
|
||||
archiveName := path.Join(c.String("target"), c.String("archive-name"))
|
||||
log.Info("Packing backup files to: %s", archiveName)
|
||||
|
||||
z, err := zip.Create(archiveName)
|
||||
if err != nil {
|
||||
log.Fatal(0, "Fail to create backup archive '%s': %v", archiveName, err)
|
||||
}
|
||||
if err = z.AddFile(_ARCHIVE_ROOT_DIR+"/metadata.ini", metaFile); err != nil {
|
||||
log.Fatal(0, "Fail to include 'metadata.ini': %v", err)
|
||||
}
|
||||
|
||||
// Database
|
||||
dbDir := path.Join(rootDir, "db")
|
||||
if err = models.DumpDatabase(dbDir); err != nil {
|
||||
log.Fatal(0, "Fail to dump database: %v", err)
|
||||
}
|
||||
if err = z.AddDir(_ARCHIVE_ROOT_DIR+"/db", dbDir); err != nil {
|
||||
log.Fatal(0, "Fail to include 'db': %v", err)
|
||||
}
|
||||
|
||||
// Custom files
|
||||
if !c.Bool("database-only") {
|
||||
if err = z.AddDir(_ARCHIVE_ROOT_DIR+"/custom", setting.CustomPath); err != nil {
|
||||
log.Fatal(0, "Fail to include 'custom': %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Data files
|
||||
if !c.Bool("database-only") {
|
||||
for _, dir := range []string{"attachments", "avatars", "repo-avatars"} {
|
||||
dirPath := path.Join(setting.AppDataPath, dir)
|
||||
if !com.IsDir(dirPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = z.AddDir(path.Join(_ARCHIVE_ROOT_DIR+"/data", dir), dirPath); err != nil {
|
||||
log.Fatal(0, "Fail to include 'data': %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repositories
|
||||
if !c.Bool("exclude-repos") && !c.Bool("database-only") {
|
||||
reposDump := path.Join(rootDir, "repositories.zip")
|
||||
log.Info("Dumping repositories in '%s'", setting.RepoRootPath)
|
||||
if err = zip.PackTo(setting.RepoRootPath, reposDump, true); err != nil {
|
||||
log.Fatal(0, "Fail to dump repositories: %v", err)
|
||||
}
|
||||
log.Info("Repositories dumped to: %s", reposDump)
|
||||
|
||||
if err = z.AddFile(_ARCHIVE_ROOT_DIR+"/repositories.zip", reposDump); err != nil {
|
||||
log.Fatal(0, "Fail to include 'repositories.zip': %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = z.Close(); err != nil {
|
||||
log.Fatal(0, "Fail to save backup archive '%s': %v", archiveName, err)
|
||||
}
|
||||
|
||||
os.RemoveAll(rootDir)
|
||||
log.Info("Backup succeed! Archive is located at: %s", archiveName)
|
||||
log.Shutdown()
|
||||
return nil
|
||||
}
|
||||
163
cmd/cert.go
163
cmd/cert.go
@@ -1,163 +0,0 @@
|
||||
// +build cert
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var Cert = cli.Command{
|
||||
Name: "cert",
|
||||
Usage: "Generate self-signed certificate",
|
||||
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
||||
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
||||
Action: runCert,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("host", "", "Comma-separated hostnames and IPs to generate a certificate for"),
|
||||
stringFlag("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521"),
|
||||
intFlag("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set"),
|
||||
stringFlag("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011"),
|
||||
durationFlag("duration", 365*24*time.Hour, "Duration that certificate is valid for"),
|
||||
boolFlag("ca", "whether this cert should be its own Certificate Authority"),
|
||||
},
|
||||
}
|
||||
|
||||
func publicKey(priv interface{}) interface{} {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
case *ecdsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func pemBlockForKey(priv interface{}) *pem.Block {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
||||
case *ecdsa.PrivateKey:
|
||||
b, err := x509.MarshalECPrivateKey(k)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to marshal ECDSA private key: %v\n", err)
|
||||
}
|
||||
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func runCert(ctx *cli.Context) error {
|
||||
if len(ctx.String("host")) == 0 {
|
||||
log.Fatal("Missing required --host parameter")
|
||||
}
|
||||
|
||||
var priv interface{}
|
||||
var err error
|
||||
switch ctx.String("ecdsa-curve") {
|
||||
case "":
|
||||
priv, err = rsa.GenerateKey(rand.Reader, ctx.Int("rsa-bits"))
|
||||
case "P224":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
case "P256":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case "P384":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case "P521":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
default:
|
||||
log.Fatalf("Unrecognized elliptic curve: %q", ctx.String("ecdsa-curve"))
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate private key: %s", err)
|
||||
}
|
||||
|
||||
var notBefore time.Time
|
||||
if len(ctx.String("start-date")) == 0 {
|
||||
notBefore = time.Now()
|
||||
} else {
|
||||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", ctx.String("start-date"))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse creation date: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
notAfter := notBefore.Add(ctx.Duration("duration"))
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate serial number: %s", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Acme Co"},
|
||||
CommonName: "Gogs",
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
hosts := strings.Split(ctx.String("host"), ",")
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.Bool("ca") {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create certificate: %s", err)
|
||||
}
|
||||
|
||||
certOut, err := os.Create("cert.pem")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open cert.pem for writing: %s", err)
|
||||
}
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
log.Println("Written cert.pem")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open key.pem for writing: %v\n", err)
|
||||
}
|
||||
pem.Encode(keyOut, pemBlockForKey(priv))
|
||||
keyOut.Close()
|
||||
log.Println("Written key.pem")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// +build !cert
|
||||
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var Cert = cli.Command{
|
||||
Name: "cert",
|
||||
Usage: "Generate self-signed certificate",
|
||||
Description: `Please use build tags "cert" to rebuild Gogs in order to have this ability`,
|
||||
Action: runCert,
|
||||
}
|
||||
|
||||
func runCert(ctx *cli.Context) error {
|
||||
fmt.Println("Command cert not available, please use build tags 'cert' to rebuild.")
|
||||
os.Exit(1)
|
||||
|
||||
return nil
|
||||
}
|
||||
42
cmd/cmd.go
42
cmd/cmd.go
@@ -1,42 +0,0 @@
|
||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func stringFlag(name, value, usage string) cli.StringFlag {
|
||||
return cli.StringFlag{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Usage: usage,
|
||||
}
|
||||
}
|
||||
|
||||
func boolFlag(name, usage string) cli.BoolFlag {
|
||||
return cli.BoolFlag{
|
||||
Name: name,
|
||||
Usage: usage,
|
||||
}
|
||||
}
|
||||
|
||||
func intFlag(name string, value int, usage string) cli.IntFlag {
|
||||
return cli.IntFlag{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Usage: usage,
|
||||
}
|
||||
}
|
||||
|
||||
func durationFlag(name string, value time.Duration, usage string) cli.DurationFlag {
|
||||
return cli.DurationFlag{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Usage: usage,
|
||||
}
|
||||
}
|
||||
277
cmd/hook.go
277
cmd/hook.go
@@ -1,277 +0,0 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogs/git-module"
|
||||
|
||||
"gogs.io/gogs/models"
|
||||
"gogs.io/gogs/models/errors"
|
||||
"gogs.io/gogs/pkg/httplib"
|
||||
"gogs.io/gogs/pkg/mailer"
|
||||
"gogs.io/gogs/pkg/setting"
|
||||
"gogs.io/gogs/pkg/template"
|
||||
)
|
||||
|
||||
var (
|
||||
Hook = cli.Command{
|
||||
Name: "hook",
|
||||
Usage: "Delegate commands to corresponding Git hooks",
|
||||
Description: "All sub-commands should only be called by Git",
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
Subcommands: []cli.Command{
|
||||
subcmdHookPreReceive,
|
||||
subcmdHookUpadte,
|
||||
subcmdHookPostReceive,
|
||||
},
|
||||
}
|
||||
|
||||
subcmdHookPreReceive = cli.Command{
|
||||
Name: "pre-receive",
|
||||
Usage: "Delegate pre-receive Git hook",
|
||||
Description: "This command should only be called by Git",
|
||||
Action: runHookPreReceive,
|
||||
}
|
||||
subcmdHookUpadte = cli.Command{
|
||||
Name: "update",
|
||||
Usage: "Delegate update Git hook",
|
||||
Description: "This command should only be called by Git",
|
||||
Action: runHookUpdate,
|
||||
}
|
||||
subcmdHookPostReceive = cli.Command{
|
||||
Name: "post-receive",
|
||||
Usage: "Delegate post-receive Git hook",
|
||||
Description: "This command should only be called by Git",
|
||||
Action: runHookPostReceive,
|
||||
}
|
||||
)
|
||||
|
||||
func runHookPreReceive(c *cli.Context) error {
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
return nil
|
||||
}
|
||||
setup(c, "hooks/pre-receive.log", true)
|
||||
|
||||
isWiki := strings.Contains(os.Getenv(models.ENV_REPO_CUSTOM_HOOKS_PATH), ".wiki.git/")
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
buf.Write(scanner.Bytes())
|
||||
buf.WriteByte('\n')
|
||||
|
||||
if isWiki {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := bytes.Fields(scanner.Bytes())
|
||||
if len(fields) != 3 {
|
||||
continue
|
||||
}
|
||||
oldCommitID := string(fields[0])
|
||||
newCommitID := string(fields[1])
|
||||
branchName := strings.TrimPrefix(string(fields[2]), git.BRANCH_PREFIX)
|
||||
|
||||
// Branch protection
|
||||
repoID := com.StrTo(os.Getenv(models.ENV_REPO_ID)).MustInt64()
|
||||
protectBranch, err := models.GetProtectBranchOfRepoByName(repoID, branchName)
|
||||
if err != nil {
|
||||
if errors.IsErrBranchNotExist(err) {
|
||||
continue
|
||||
}
|
||||
fail("Internal error", "GetProtectBranchOfRepoByName [repo_id: %d, branch: %s]: %v", repoID, branchName, err)
|
||||
}
|
||||
if !protectBranch.Protected {
|
||||
continue
|
||||
}
|
||||
|
||||
// Whitelist users can bypass require pull request check
|
||||
bypassRequirePullRequest := false
|
||||
|
||||
// Check if user is in whitelist when enabled
|
||||
userID := com.StrTo(os.Getenv(models.ENV_AUTH_USER_ID)).MustInt64()
|
||||
if protectBranch.EnableWhitelist {
|
||||
if !models.IsUserInProtectBranchWhitelist(repoID, userID, branchName) {
|
||||
fail(fmt.Sprintf("Branch '%s' is protected and you are not in the push whitelist", branchName), "")
|
||||
}
|
||||
|
||||
bypassRequirePullRequest = true
|
||||
}
|
||||
|
||||
// Check if branch allows direct push
|
||||
if !bypassRequirePullRequest && protectBranch.RequirePullRequest {
|
||||
fail(fmt.Sprintf("Branch '%s' is protected and commits must be merged through pull request", branchName), "")
|
||||
}
|
||||
|
||||
// check and deletion
|
||||
if newCommitID == git.EMPTY_SHA {
|
||||
fail(fmt.Sprintf("Branch '%s' is protected from deletion", branchName), "")
|
||||
}
|
||||
|
||||
// Check force push
|
||||
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).
|
||||
RunInDir(models.RepoPath(os.Getenv(models.ENV_REPO_OWNER_NAME), os.Getenv(models.ENV_REPO_NAME)))
|
||||
if err != nil {
|
||||
fail("Internal error", "Fail to detect force push: %v", err)
|
||||
} else if len(output) > 0 {
|
||||
fail(fmt.Sprintf("Branch '%s' is protected from force push", branchName), "")
|
||||
}
|
||||
}
|
||||
|
||||
customHooksPath := filepath.Join(os.Getenv(models.ENV_REPO_CUSTOM_HOOKS_PATH), "pre-receive")
|
||||
if !com.IsFile(customHooksPath) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var hookCmd *exec.Cmd
|
||||
if setting.IsWindows {
|
||||
hookCmd = exec.Command("bash.exe", "custom_hooks/pre-receive")
|
||||
} else {
|
||||
hookCmd = exec.Command(customHooksPath)
|
||||
}
|
||||
hookCmd.Dir = models.RepoPath(os.Getenv(models.ENV_REPO_OWNER_NAME), os.Getenv(models.ENV_REPO_NAME))
|
||||
hookCmd.Stdout = os.Stdout
|
||||
hookCmd.Stdin = buf
|
||||
hookCmd.Stderr = os.Stderr
|
||||
if err := hookCmd.Run(); err != nil {
|
||||
fail("Internal error", "Fail to execute custom pre-receive hook: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runHookUpdate(c *cli.Context) error {
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
return nil
|
||||
}
|
||||
setup(c, "hooks/update.log", false)
|
||||
|
||||
args := c.Args()
|
||||
if len(args) != 3 {
|
||||
fail("Arguments received are not equal to three", "Arguments received are not equal to three")
|
||||
} else if len(args[0]) == 0 {
|
||||
fail("First argument 'refName' is empty", "First argument 'refName' is empty")
|
||||
}
|
||||
|
||||
customHooksPath := filepath.Join(os.Getenv(models.ENV_REPO_CUSTOM_HOOKS_PATH), "update")
|
||||
if !com.IsFile(customHooksPath) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var hookCmd *exec.Cmd
|
||||
if setting.IsWindows {
|
||||
hookCmd = exec.Command("bash.exe", append([]string{"custom_hooks/update"}, args...)...)
|
||||
} else {
|
||||
hookCmd = exec.Command(customHooksPath, args...)
|
||||
}
|
||||
hookCmd.Dir = models.RepoPath(os.Getenv(models.ENV_REPO_OWNER_NAME), os.Getenv(models.ENV_REPO_NAME))
|
||||
hookCmd.Stdout = os.Stdout
|
||||
hookCmd.Stdin = os.Stdin
|
||||
hookCmd.Stderr = os.Stderr
|
||||
if err := hookCmd.Run(); err != nil {
|
||||
fail("Internal error", "Fail to execute custom pre-receive hook: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runHookPostReceive(c *cli.Context) error {
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
return nil
|
||||
}
|
||||
setup(c, "hooks/post-receive.log", true)
|
||||
|
||||
// Post-receive hook does more than just gather Git information,
|
||||
// so we need to setup additional services for email notifications.
|
||||
setting.NewPostReceiveHookServices()
|
||||
mailer.NewContext()
|
||||
mailer.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"),
|
||||
path.Join(setting.CustomPath, "templates/mail"), template.NewFuncMap())
|
||||
|
||||
isWiki := strings.Contains(os.Getenv(models.ENV_REPO_CUSTOM_HOOKS_PATH), ".wiki.git/")
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
buf.Write(scanner.Bytes())
|
||||
buf.WriteByte('\n')
|
||||
|
||||
// TODO: support news feeds for wiki
|
||||
if isWiki {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := bytes.Fields(scanner.Bytes())
|
||||
if len(fields) != 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
options := models.PushUpdateOptions{
|
||||
OldCommitID: string(fields[0]),
|
||||
NewCommitID: string(fields[1]),
|
||||
RefFullName: string(fields[2]),
|
||||
PusherID: com.StrTo(os.Getenv(models.ENV_AUTH_USER_ID)).MustInt64(),
|
||||
PusherName: os.Getenv(models.ENV_AUTH_USER_NAME),
|
||||
RepoUserName: os.Getenv(models.ENV_REPO_OWNER_NAME),
|
||||
RepoName: os.Getenv(models.ENV_REPO_NAME),
|
||||
}
|
||||
if err := models.PushUpdate(options); err != nil {
|
||||
log.Error(2, "PushUpdate: %v", err)
|
||||
}
|
||||
|
||||
// Ask for running deliver hook and test pull request tasks
|
||||
reqURL := setting.LocalURL + options.RepoUserName + "/" + options.RepoName + "/tasks/trigger?branch=" +
|
||||
template.EscapePound(strings.TrimPrefix(options.RefFullName, git.BRANCH_PREFIX)) +
|
||||
"&secret=" + os.Getenv(models.ENV_REPO_OWNER_SALT_MD5) +
|
||||
"&pusher=" + os.Getenv(models.ENV_AUTH_USER_ID)
|
||||
log.Trace("Trigger task: %s", reqURL)
|
||||
|
||||
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}).Response()
|
||||
if err == nil {
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode/100 != 2 {
|
||||
log.Error(2, "Fail to trigger task: not 2xx response code")
|
||||
}
|
||||
} else {
|
||||
log.Error(2, "Fail to trigger task: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
customHooksPath := filepath.Join(os.Getenv(models.ENV_REPO_CUSTOM_HOOKS_PATH), "post-receive")
|
||||
if !com.IsFile(customHooksPath) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var hookCmd *exec.Cmd
|
||||
if setting.IsWindows {
|
||||
hookCmd = exec.Command("bash.exe", "custom_hooks/post-receive")
|
||||
} else {
|
||||
hookCmd = exec.Command(customHooksPath)
|
||||
}
|
||||
hookCmd.Dir = models.RepoPath(os.Getenv(models.ENV_REPO_OWNER_NAME), os.Getenv(models.ENV_REPO_NAME))
|
||||
hookCmd.Stdout = os.Stdout
|
||||
hookCmd.Stdin = buf
|
||||
hookCmd.Stderr = os.Stderr
|
||||
if err := hookCmd.Run(); err != nil {
|
||||
fail("Internal error", "Fail to execute custom post-receive hook: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
113
cmd/import.go
113
cmd/import.go
@@ -1,113 +0,0 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"gogs.io/gogs/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
Import = cli.Command{
|
||||
Name: "import",
|
||||
Usage: "Import portable data as local Gogs data",
|
||||
Description: `Allow user import data from other Gogs installations to local instance
|
||||
without manually hacking the data files`,
|
||||
Subcommands: []cli.Command{
|
||||
subcmdImportLocale,
|
||||
},
|
||||
}
|
||||
|
||||
subcmdImportLocale = cli.Command{
|
||||
Name: "locale",
|
||||
Usage: "Import locale files to local repository",
|
||||
Action: runImportLocale,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("source", "", "Source directory that stores new locale files"),
|
||||
stringFlag("target", "", "Target directory that stores old locale files"),
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runImportLocale(c *cli.Context) error {
|
||||
if !c.IsSet("source") {
|
||||
return fmt.Errorf("Source directory is not specified")
|
||||
} else if !c.IsSet("target") {
|
||||
return fmt.Errorf("Target directory is not specified")
|
||||
}
|
||||
if !com.IsDir(c.String("source")) {
|
||||
return fmt.Errorf("Source directory does not exist or is not a directory")
|
||||
} else if !com.IsDir(c.String("target")) {
|
||||
return fmt.Errorf("Target directory does not exist or is not a directory")
|
||||
}
|
||||
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
setting.NewContext()
|
||||
|
||||
now := time.Now()
|
||||
|
||||
line := make([]byte, 0, 100)
|
||||
badChars := []byte(`="`)
|
||||
escapedQuotes := []byte(`\"`)
|
||||
regularQuotes := []byte(`"`)
|
||||
// Cut out en-US.
|
||||
for _, lang := range setting.Langs[1:] {
|
||||
name := fmt.Sprintf("locale_%s.ini", lang)
|
||||
source := filepath.Join(c.String("source"), name)
|
||||
target := filepath.Join(c.String("target"), name)
|
||||
if !com.IsFile(source) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Crowdin surrounds double quotes for strings contain quotes inside,
|
||||
// this breaks INI parser, we need to fix that.
|
||||
sr, err := os.Open(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Open: %v", err)
|
||||
}
|
||||
|
||||
tw, err := os.Create(target)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
return fmt.Errorf("Open: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(sr)
|
||||
for scanner.Scan() {
|
||||
line = scanner.Bytes()
|
||||
idx := bytes.Index(line, badChars)
|
||||
if idx > -1 && line[len(line)-1] == '"' {
|
||||
// We still want the "=" sign
|
||||
line = append(line[:idx+1], line[idx+2:len(line)-1]...)
|
||||
line = bytes.Replace(line, escapedQuotes, regularQuotes, -1)
|
||||
}
|
||||
tw.Write(line)
|
||||
tw.WriteString("\n")
|
||||
}
|
||||
sr.Close()
|
||||
tw.Close()
|
||||
|
||||
// Modification time of files from Crowdin often ahead of current,
|
||||
// so we need to set back to current.
|
||||
os.Chtimes(target, now, now)
|
||||
}
|
||||
|
||||
fmt.Println("Locale files has been successfully imported!")
|
||||
return nil
|
||||
}
|
||||
148
cmd/restore.go
148
cmd/restore.go
@@ -1,148 +0,0 @@
|
||||
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/unknwon/cae/zip"
|
||||
"github.com/unknwon/com"
|
||||
"github.com/mcuadros/go-version"
|
||||
"github.com/urfave/cli"
|
||||
log "gopkg.in/clog.v1"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"gogs.io/gogs/models"
|
||||
"gogs.io/gogs/pkg/setting"
|
||||
)
|
||||
|
||||
var Restore = cli.Command{
|
||||
Name: "restore",
|
||||
Usage: "Restore files and database from backup",
|
||||
Description: `Restore imports all related files and database from a backup archive.
|
||||
The backup version must lower or equal to current Gogs version. You can also import
|
||||
backup from other database engines, which is useful for database migrating.
|
||||
|
||||
If corresponding files or database tables are not presented in the archive, they will
|
||||
be skipped and remain unchanged.`,
|
||||
Action: runRestore,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
boolFlag("verbose, v", "Show process details"),
|
||||
stringFlag("tempdir, t", os.TempDir(), "Temporary directory path"),
|
||||
stringFlag("from", "", "Path to backup archive"),
|
||||
boolFlag("database-only", "Only import database"),
|
||||
boolFlag("exclude-repos", "Exclude repositories"),
|
||||
},
|
||||
}
|
||||
|
||||
// lastSupportedVersionOfFormat returns the last supported version of the backup archive
|
||||
// format that is able to import.
|
||||
var lastSupportedVersionOfFormat = map[int]string{}
|
||||
|
||||
func runRestore(c *cli.Context) error {
|
||||
zip.Verbose = c.Bool("verbose")
|
||||
|
||||
tmpDir := c.String("tempdir")
|
||||
if !com.IsExist(tmpDir) {
|
||||
log.Fatal(0, "'--tempdir' does not exist: %s", tmpDir)
|
||||
}
|
||||
|
||||
log.Info("Restore backup from: %s", c.String("from"))
|
||||
if err := zip.ExtractTo(c.String("from"), tmpDir); err != nil {
|
||||
log.Fatal(0, "Failed to extract backup archive: %v", err)
|
||||
}
|
||||
archivePath := path.Join(tmpDir, _ARCHIVE_ROOT_DIR)
|
||||
defer os.RemoveAll(archivePath)
|
||||
|
||||
// Check backup version
|
||||
metaFile := path.Join(archivePath, "metadata.ini")
|
||||
if !com.IsExist(metaFile) {
|
||||
log.Fatal(0, "File 'metadata.ini' is missing")
|
||||
}
|
||||
metadata, err := ini.Load(metaFile)
|
||||
if err != nil {
|
||||
log.Fatal(0, "Failed to load metadata '%s': %v", metaFile, err)
|
||||
}
|
||||
backupVersion := metadata.Section("").Key("GOGS_VERSION").MustString("999.0")
|
||||
if version.Compare(setting.AppVer, backupVersion, "<") {
|
||||
log.Fatal(0, "Current Gogs version is lower than backup version: %s < %s", setting.AppVer, backupVersion)
|
||||
}
|
||||
formatVersion := metadata.Section("").Key("VERSION").MustInt()
|
||||
if formatVersion == 0 {
|
||||
log.Fatal(0, "Failed to determine the backup format version from metadata '%s': %s", metaFile, "VERSION is not presented")
|
||||
}
|
||||
if formatVersion != _CURRENT_BACKUP_FORMAT_VERSION {
|
||||
log.Fatal(0, "Backup format version found is %d but this binary only supports %d\nThe last known version that is able to import your backup is %s",
|
||||
formatVersion, _CURRENT_BACKUP_FORMAT_VERSION, lastSupportedVersionOfFormat[formatVersion])
|
||||
}
|
||||
|
||||
// If config file is not present in backup, user must set this file via flag.
|
||||
// Otherwise, it's optional to set config file flag.
|
||||
configFile := path.Join(archivePath, "custom/conf/app.ini")
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
} else if !com.IsExist(configFile) {
|
||||
log.Fatal(0, "'--config' is not specified and custom config file is not found in backup")
|
||||
} else {
|
||||
setting.CustomConf = configFile
|
||||
}
|
||||
setting.NewContext()
|
||||
models.LoadConfigs()
|
||||
models.SetEngine()
|
||||
|
||||
// Database
|
||||
dbDir := path.Join(archivePath, "db")
|
||||
if err = models.ImportDatabase(dbDir, c.Bool("verbose")); err != nil {
|
||||
log.Fatal(0, "Failed to import database: %v", err)
|
||||
}
|
||||
|
||||
// Custom files
|
||||
if !c.Bool("database-only") {
|
||||
if com.IsExist(setting.CustomPath) {
|
||||
if err = os.Rename(setting.CustomPath, setting.CustomPath+".bak"); err != nil {
|
||||
log.Fatal(0, "Failed to backup current 'custom': %v", err)
|
||||
}
|
||||
}
|
||||
if err = os.Rename(path.Join(archivePath, "custom"), setting.CustomPath); err != nil {
|
||||
log.Fatal(0, "Failed to import 'custom': %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Data files
|
||||
if !c.Bool("database-only") {
|
||||
os.MkdirAll(setting.AppDataPath, os.ModePerm)
|
||||
for _, dir := range []string{"attachments", "avatars", "repo-avatars"} {
|
||||
// Skip if backup archive does not have corresponding data
|
||||
srcPath := path.Join(archivePath, "data", dir)
|
||||
if !com.IsDir(srcPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
dirPath := path.Join(setting.AppDataPath, dir)
|
||||
if com.IsExist(dirPath) {
|
||||
if err = os.Rename(dirPath, dirPath+".bak"); err != nil {
|
||||
log.Fatal(0, "Failed to backup current 'data': %v", err)
|
||||
}
|
||||
}
|
||||
if err = os.Rename(srcPath, dirPath); err != nil {
|
||||
log.Fatal(0, "Failed to import 'data': %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Repositories
|
||||
reposPath := path.Join(archivePath, "repositories.zip")
|
||||
if !c.Bool("exclude-repos") && !c.Bool("database-only") && com.IsExist(reposPath) {
|
||||
if err := zip.ExtractTo(reposPath, path.Dir(setting.RepoRootPath)); err != nil {
|
||||
log.Fatal(0, "Failed to extract 'repositories.zip': %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Restore succeed!")
|
||||
log.Shutdown()
|
||||
return nil
|
||||
}
|
||||
272
cmd/serv.go
272
cmd/serv.go
@@ -1,272 +0,0 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"gogs.io/gogs/models"
|
||||
"gogs.io/gogs/models/errors"
|
||||
"gogs.io/gogs/pkg/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
_ACCESS_DENIED_MESSAGE = "Repository does not exist or you do not have access"
|
||||
)
|
||||
|
||||
var Serv = cli.Command{
|
||||
Name: "serv",
|
||||
Usage: "This command should only be called by SSH shell",
|
||||
Description: `Serv provide access auth for repositories`,
|
||||
Action: runServ,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
|
||||
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||
fmt.Fprintln(os.Stderr, "Gogs:", userMessage)
|
||||
|
||||
if len(logMessage) > 0 {
|
||||
if !setting.ProdMode {
|
||||
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||
}
|
||||
log.Fatal(3, logMessage, args...)
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func setup(c *cli.Context, logPath string, connectDB bool) {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
} else if c.GlobalIsSet("config") {
|
||||
setting.CustomConf = c.GlobalString("config")
|
||||
}
|
||||
|
||||
setting.NewContext()
|
||||
|
||||
level := log.TRACE
|
||||
if setting.ProdMode {
|
||||
level = log.ERROR
|
||||
}
|
||||
log.New(log.FILE, log.FileConfig{
|
||||
Level: level,
|
||||
Filename: filepath.Join(setting.LogRootPath, logPath),
|
||||
FileRotationConfig: log.FileRotationConfig{
|
||||
Rotate: true,
|
||||
Daily: true,
|
||||
MaxDays: 3,
|
||||
},
|
||||
})
|
||||
log.Delete(log.CONSOLE) // Remove primary logger
|
||||
|
||||
if !connectDB {
|
||||
return
|
||||
}
|
||||
|
||||
models.LoadConfigs()
|
||||
|
||||
if setting.UseSQLite3 {
|
||||
workDir, _ := setting.WorkDir()
|
||||
os.Chdir(workDir)
|
||||
}
|
||||
|
||||
if err := models.SetEngine(); err != nil {
|
||||
fail("Internal error", "SetEngine: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func parseSSHCmd(cmd string) (string, string) {
|
||||
ss := strings.SplitN(cmd, " ", 2)
|
||||
if len(ss) != 2 {
|
||||
return "", ""
|
||||
}
|
||||
return ss[0], strings.Replace(ss[1], "'/", "'", 1)
|
||||
}
|
||||
|
||||
func checkDeployKey(key *models.PublicKey, repo *models.Repository) {
|
||||
// Check if this deploy key belongs to current repository.
|
||||
if !models.HasDeployKey(key.ID, repo.ID) {
|
||||
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
|
||||
}
|
||||
|
||||
// Update deploy key activity.
|
||||
deployKey, err := models.GetDeployKeyByRepo(key.ID, repo.ID)
|
||||
if err != nil {
|
||||
fail("Internal error", "GetDeployKey: %v", err)
|
||||
}
|
||||
|
||||
deployKey.Updated = time.Now()
|
||||
if err = models.UpdateDeployKey(deployKey); err != nil {
|
||||
fail("Internal error", "UpdateDeployKey: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
allowedCommands = map[string]models.AccessMode{
|
||||
"git-upload-pack": models.ACCESS_MODE_READ,
|
||||
"git-upload-archive": models.ACCESS_MODE_READ,
|
||||
"git-receive-pack": models.ACCESS_MODE_WRITE,
|
||||
}
|
||||
)
|
||||
|
||||
func runServ(c *cli.Context) error {
|
||||
setup(c, "serv.log", true)
|
||||
|
||||
if setting.SSH.Disabled {
|
||||
println("Gogs: SSH has been disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(c.Args()) < 1 {
|
||||
fail("Not enough arguments", "Not enough arguments")
|
||||
}
|
||||
|
||||
sshCmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||
if len(sshCmd) == 0 {
|
||||
println("Hi there, You've successfully authenticated, but Gogs does not provide shell access.")
|
||||
println("If this is unexpected, please log in with password and setup Gogs under another user.")
|
||||
return nil
|
||||
}
|
||||
|
||||
verb, args := parseSSHCmd(sshCmd)
|
||||
repoFullName := strings.ToLower(strings.Trim(args, "'"))
|
||||
repoFields := strings.SplitN(repoFullName, "/", 2)
|
||||
if len(repoFields) != 2 {
|
||||
fail("Invalid repository path", "Invalid repository path: %v", args)
|
||||
}
|
||||
ownerName := strings.ToLower(repoFields[0])
|
||||
repoName := strings.TrimSuffix(strings.ToLower(repoFields[1]), ".git")
|
||||
repoName = strings.TrimSuffix(repoName, ".wiki")
|
||||
|
||||
owner, err := models.GetUserByName(ownerName)
|
||||
if err != nil {
|
||||
if errors.IsUserNotExist(err) {
|
||||
fail("Repository owner does not exist", "Unregistered owner: %s", ownerName)
|
||||
}
|
||||
fail("Internal error", "Fail to get repository owner '%s': %v", ownerName, err)
|
||||
}
|
||||
|
||||
repo, err := models.GetRepositoryByName(owner.ID, repoName)
|
||||
if err != nil {
|
||||
if errors.IsRepoNotExist(err) {
|
||||
fail(_ACCESS_DENIED_MESSAGE, "Repository does not exist: %s/%s", owner.Name, repoName)
|
||||
}
|
||||
fail("Internal error", "Fail to get repository: %v", err)
|
||||
}
|
||||
repo.Owner = owner
|
||||
|
||||
requestMode, ok := allowedCommands[verb]
|
||||
if !ok {
|
||||
fail("Unknown git command", "Unknown git command '%s'", verb)
|
||||
}
|
||||
|
||||
// Prohibit push to mirror repositories.
|
||||
if requestMode > models.ACCESS_MODE_READ && repo.IsMirror {
|
||||
fail("Mirror repository is read-only", "")
|
||||
}
|
||||
|
||||
// Allow anonymous (user is nil) clone for public repositories.
|
||||
var user *models.User
|
||||
|
||||
key, err := models.GetPublicKeyByID(com.StrTo(strings.TrimPrefix(c.Args()[0], "key-")).MustInt64())
|
||||
if err != nil {
|
||||
fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err)
|
||||
}
|
||||
|
||||
if requestMode == models.ACCESS_MODE_WRITE || repo.IsPrivate {
|
||||
// Check deploy key or user key.
|
||||
if key.IsDeployKey() {
|
||||
if key.Mode < requestMode {
|
||||
fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
|
||||
}
|
||||
checkDeployKey(key, repo)
|
||||
} else {
|
||||
user, err = models.GetUserByKeyID(key.ID)
|
||||
if err != nil {
|
||||
fail("Internal error", "Fail to get user by key ID '%d': %v", key.ID, err)
|
||||
}
|
||||
|
||||
mode, err := models.UserAccessMode(user.ID, repo)
|
||||
if err != nil {
|
||||
fail("Internal error", "Fail to check access: %v", err)
|
||||
}
|
||||
|
||||
if mode < requestMode {
|
||||
clientMessage := _ACCESS_DENIED_MESSAGE
|
||||
if mode >= models.ACCESS_MODE_READ {
|
||||
clientMessage = "You do not have sufficient authorization for this action"
|
||||
}
|
||||
fail(clientMessage,
|
||||
"User '%s' does not have level '%v' access to repository '%s'",
|
||||
user.Name, requestMode, repoFullName)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setting.NewService()
|
||||
// Check if the key can access to the repository in case of it is a deploy key (a deploy keys != user key).
|
||||
// A deploy key doesn't represent a signed in user, so in a site with Service.RequireSignInView activated
|
||||
// we should give read access only in repositories where this deploy key is in use. In other case, a server
|
||||
// or system using an active deploy key can get read access to all the repositories in a Gogs service.
|
||||
if key.IsDeployKey() && setting.Service.RequireSignInView {
|
||||
checkDeployKey(key, repo)
|
||||
}
|
||||
}
|
||||
|
||||
// Update user key activity.
|
||||
if key.ID > 0 {
|
||||
key, err := models.GetPublicKeyByID(key.ID)
|
||||
if err != nil {
|
||||
fail("Internal error", "GetPublicKeyByID: %v", err)
|
||||
}
|
||||
|
||||
key.Updated = time.Now()
|
||||
if err = models.UpdatePublicKey(key); err != nil {
|
||||
fail("Internal error", "UpdatePublicKey: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Special handle for Windows.
|
||||
if setting.IsWindows {
|
||||
verb = strings.Replace(verb, "-", " ", 1)
|
||||
}
|
||||
|
||||
var gitCmd *exec.Cmd
|
||||
verbs := strings.Split(verb, " ")
|
||||
if len(verbs) == 2 {
|
||||
gitCmd = exec.Command(verbs[0], verbs[1], repoFullName)
|
||||
} else {
|
||||
gitCmd = exec.Command(verb, repoFullName)
|
||||
}
|
||||
if requestMode == models.ACCESS_MODE_WRITE {
|
||||
gitCmd.Env = append(os.Environ(), models.ComposeHookEnvs(models.ComposeHookEnvsOptions{
|
||||
AuthUser: user,
|
||||
OwnerName: owner.Name,
|
||||
OwnerSalt: owner.Salt,
|
||||
RepoID: repo.ID,
|
||||
RepoName: repo.Name,
|
||||
RepoPath: repo.RepoPath(),
|
||||
})...)
|
||||
}
|
||||
gitCmd.Dir = setting.RepoRootPath
|
||||
gitCmd.Stdout = os.Stdout
|
||||
gitCmd.Stdin = os.Stdin
|
||||
gitCmd.Stderr = os.Stderr
|
||||
if err = gitCmd.Run(); err != nil {
|
||||
fail("Internal error", "Fail to execute git command: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
757
cmd/web.go
757
cmd/web.go
@@ -1,757 +0,0 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/go-macaron/cache"
|
||||
"github.com/go-macaron/captcha"
|
||||
"github.com/go-macaron/csrf"
|
||||
"github.com/go-macaron/gzip"
|
||||
"github.com/go-macaron/i18n"
|
||||
"github.com/go-macaron/session"
|
||||
"github.com/go-macaron/toolbox"
|
||||
"github.com/mcuadros/go-version"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/urfave/cli"
|
||||
log "gopkg.in/clog.v1"
|
||||
"gopkg.in/macaron.v1"
|
||||
|
||||
"gogs.io/gogs/models"
|
||||
"gogs.io/gogs/pkg/bindata"
|
||||
"gogs.io/gogs/pkg/context"
|
||||
"gogs.io/gogs/pkg/form"
|
||||
"gogs.io/gogs/pkg/mailer"
|
||||
"gogs.io/gogs/pkg/setting"
|
||||
"gogs.io/gogs/pkg/template"
|
||||
"gogs.io/gogs/routes"
|
||||
"gogs.io/gogs/routes/admin"
|
||||
apiv1 "gogs.io/gogs/routes/api/v1"
|
||||
"gogs.io/gogs/routes/dev"
|
||||
"gogs.io/gogs/routes/org"
|
||||
"gogs.io/gogs/routes/repo"
|
||||
"gogs.io/gogs/routes/user"
|
||||
)
|
||||
|
||||
var Web = cli.Command{
|
||||
Name: "web",
|
||||
Usage: "Start web server",
|
||||
Description: `Gogs web server is the only thing you need to run,
|
||||
and it takes care of all the other things for you`,
|
||||
Action: runWeb,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("port, p", "3000", "Temporary port number to prevent conflict"),
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
|
||||
// checkVersion checks if binary matches the version of templates files.
|
||||
func checkVersion() {
|
||||
// Templates.
|
||||
data, err := ioutil.ReadFile(setting.StaticRootPath + "/templates/.VERSION")
|
||||
if err != nil {
|
||||
log.Fatal(2, "Fail to read 'templates/.VERSION': %v", err)
|
||||
}
|
||||
tplVer := strings.TrimSpace(string(data))
|
||||
if tplVer != setting.AppVer {
|
||||
if version.Compare(tplVer, setting.AppVer, ">") {
|
||||
log.Fatal(2, "Binary version is lower than template file version, did you forget to recompile Gogs?")
|
||||
} else {
|
||||
log.Fatal(2, "Binary version is higher than template file version, did you forget to update template files?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// newMacaron initializes Macaron instance.
|
||||
func newMacaron() *macaron.Macaron {
|
||||
m := macaron.New()
|
||||
if !setting.DisableRouterLog {
|
||||
m.Use(macaron.Logger())
|
||||
}
|
||||
m.Use(macaron.Recovery())
|
||||
if setting.EnableGzip {
|
||||
m.Use(gzip.Gziper())
|
||||
}
|
||||
if setting.Protocol == setting.SCHEME_FCGI {
|
||||
m.SetURLPrefix(setting.AppSubURL)
|
||||
}
|
||||
m.Use(macaron.Static(
|
||||
path.Join(setting.StaticRootPath, "public"),
|
||||
macaron.StaticOptions{
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
},
|
||||
))
|
||||
m.Use(macaron.Static(
|
||||
setting.AvatarUploadPath,
|
||||
macaron.StaticOptions{
|
||||
Prefix: models.USER_AVATAR_URL_PREFIX,
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
},
|
||||
))
|
||||
m.Use(macaron.Static(
|
||||
setting.RepositoryAvatarUploadPath,
|
||||
macaron.StaticOptions{
|
||||
Prefix: models.REPO_AVATAR_URL_PREFIX,
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
},
|
||||
))
|
||||
|
||||
funcMap := template.NewFuncMap()
|
||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
||||
AppendDirectories: []string{path.Join(setting.CustomPath, "templates")},
|
||||
Funcs: funcMap,
|
||||
IndentJSON: macaron.Env != macaron.PROD,
|
||||
}))
|
||||
mailer.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"),
|
||||
path.Join(setting.CustomPath, "templates/mail"), funcMap)
|
||||
|
||||
localeNames, err := bindata.AssetDir("conf/locale")
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to list locale files: %v", err)
|
||||
}
|
||||
localFiles := make(map[string][]byte)
|
||||
for _, name := range localeNames {
|
||||
localFiles[name] = bindata.MustAsset("conf/locale/" + name)
|
||||
}
|
||||
m.Use(i18n.I18n(i18n.Options{
|
||||
SubURL: setting.AppSubURL,
|
||||
Files: localFiles,
|
||||
CustomDirectory: path.Join(setting.CustomPath, "conf/locale"),
|
||||
Langs: setting.Langs,
|
||||
Names: setting.Names,
|
||||
DefaultLang: "en-US",
|
||||
Redirect: true,
|
||||
}))
|
||||
m.Use(cache.Cacher(cache.Options{
|
||||
Adapter: setting.CacheAdapter,
|
||||
AdapterConfig: setting.CacheConn,
|
||||
Interval: setting.CacheInterval,
|
||||
}))
|
||||
m.Use(captcha.Captchaer(captcha.Options{
|
||||
SubURL: setting.AppSubURL,
|
||||
}))
|
||||
m.Use(session.Sessioner(setting.SessionConfig))
|
||||
m.Use(csrf.Csrfer(csrf.Options{
|
||||
Secret: setting.SecretKey,
|
||||
Cookie: setting.CSRFCookieName,
|
||||
SetCookie: true,
|
||||
Header: "X-Csrf-Token",
|
||||
CookiePath: setting.AppSubURL,
|
||||
}))
|
||||
m.Use(toolbox.Toolboxer(m, toolbox.Options{
|
||||
HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{
|
||||
&toolbox.HealthCheckFuncDesc{
|
||||
Desc: "Database connection",
|
||||
Func: models.Ping,
|
||||
},
|
||||
},
|
||||
}))
|
||||
m.Use(context.Contexter())
|
||||
return m
|
||||
}
|
||||
|
||||
func runWeb(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
routes.GlobalInit()
|
||||
checkVersion()
|
||||
|
||||
m := newMacaron()
|
||||
|
||||
reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true})
|
||||
ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView})
|
||||
ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true})
|
||||
reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true})
|
||||
|
||||
bindIgnErr := binding.BindIgnErr
|
||||
|
||||
m.SetAutoHead(true)
|
||||
|
||||
// FIXME: not all routes need go through same middlewares.
|
||||
// Especially some AJAX requests, we can reduce middleware number to improve performance.
|
||||
// Routers.
|
||||
m.Get("/", ignSignIn, routes.Home)
|
||||
m.Group("/explore", func() {
|
||||
m.Get("", func(c *context.Context) {
|
||||
c.Redirect(setting.AppSubURL + "/explore/repos")
|
||||
})
|
||||
m.Get("/repos", routes.ExploreRepos)
|
||||
m.Get("/users", routes.ExploreUsers)
|
||||
m.Get("/organizations", routes.ExploreOrganizations)
|
||||
}, ignSignIn)
|
||||
m.Combo("/install", routes.InstallInit).Get(routes.Install).
|
||||
Post(bindIgnErr(form.Install{}), routes.InstallPost)
|
||||
m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues)
|
||||
|
||||
// ***** START: User *****
|
||||
m.Group("/user", func() {
|
||||
m.Group("/login", func() {
|
||||
m.Combo("").Get(user.Login).
|
||||
Post(bindIgnErr(form.SignIn{}), user.LoginPost)
|
||||
m.Combo("/two_factor").Get(user.LoginTwoFactor).Post(user.LoginTwoFactorPost)
|
||||
m.Combo("/two_factor_recovery_code").Get(user.LoginTwoFactorRecoveryCode).Post(user.LoginTwoFactorRecoveryCodePost)
|
||||
})
|
||||
|
||||
m.Get("/sign_up", user.SignUp)
|
||||
m.Post("/sign_up", bindIgnErr(form.Register{}), user.SignUpPost)
|
||||
m.Get("/reset_password", user.ResetPasswd)
|
||||
m.Post("/reset_password", user.ResetPasswdPost)
|
||||
}, reqSignOut)
|
||||
|
||||
m.Group("/user/settings", func() {
|
||||
m.Get("", user.Settings)
|
||||
m.Post("", bindIgnErr(form.UpdateProfile{}), user.SettingsPost)
|
||||
m.Combo("/avatar").Get(user.SettingsAvatar).
|
||||
Post(binding.MultipartForm(form.Avatar{}), user.SettingsAvatarPost)
|
||||
m.Post("/avatar/delete", user.SettingsDeleteAvatar)
|
||||
m.Combo("/email").Get(user.SettingsEmails).
|
||||
Post(bindIgnErr(form.AddEmail{}), user.SettingsEmailPost)
|
||||
m.Post("/email/delete", user.DeleteEmail)
|
||||
m.Get("/password", user.SettingsPassword)
|
||||
m.Post("/password", bindIgnErr(form.ChangePassword{}), user.SettingsPasswordPost)
|
||||
m.Combo("/ssh").Get(user.SettingsSSHKeys).
|
||||
Post(bindIgnErr(form.AddSSHKey{}), user.SettingsSSHKeysPost)
|
||||
m.Post("/ssh/delete", user.DeleteSSHKey)
|
||||
m.Group("/security", func() {
|
||||
m.Get("", user.SettingsSecurity)
|
||||
m.Combo("/two_factor_enable").Get(user.SettingsTwoFactorEnable).
|
||||
Post(user.SettingsTwoFactorEnablePost)
|
||||
m.Combo("/two_factor_recovery_codes").Get(user.SettingsTwoFactorRecoveryCodes).
|
||||
Post(user.SettingsTwoFactorRecoveryCodesPost)
|
||||
m.Post("/two_factor_disable", user.SettingsTwoFactorDisable)
|
||||
})
|
||||
m.Group("/repositories", func() {
|
||||
m.Get("", user.SettingsRepos)
|
||||
m.Post("/leave", user.SettingsLeaveRepo)
|
||||
})
|
||||
m.Group("/organizations", func() {
|
||||
m.Get("", user.SettingsOrganizations)
|
||||
m.Post("/leave", user.SettingsLeaveOrganization)
|
||||
})
|
||||
m.Combo("/applications").Get(user.SettingsApplications).
|
||||
Post(bindIgnErr(form.NewAccessToken{}), user.SettingsApplicationsPost)
|
||||
m.Post("/applications/delete", user.SettingsDeleteApplication)
|
||||
m.Route("/delete", "GET,POST", user.SettingsDelete)
|
||||
}, reqSignIn, func(c *context.Context) {
|
||||
c.Data["PageIsUserSettings"] = true
|
||||
})
|
||||
|
||||
m.Group("/user", func() {
|
||||
m.Any("/activate", user.Activate)
|
||||
m.Any("/activate_email", user.ActivateEmail)
|
||||
m.Get("/email2user", user.Email2User)
|
||||
m.Get("/forget_password", user.ForgotPasswd)
|
||||
m.Post("/forget_password", user.ForgotPasswdPost)
|
||||
m.Post("/logout", user.SignOut)
|
||||
})
|
||||
// ***** END: User *****
|
||||
|
||||
reqAdmin := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true})
|
||||
|
||||
// ***** START: Admin *****
|
||||
m.Group("/admin", func() {
|
||||
m.Get("", admin.Dashboard)
|
||||
m.Get("/config", admin.Config)
|
||||
m.Post("/config/test_mail", admin.SendTestMail)
|
||||
m.Get("/monitor", admin.Monitor)
|
||||
|
||||
m.Group("/users", func() {
|
||||
m.Get("", admin.Users)
|
||||
m.Combo("/new").Get(admin.NewUser).Post(bindIgnErr(form.AdminCrateUser{}), admin.NewUserPost)
|
||||
m.Combo("/:userid").Get(admin.EditUser).Post(bindIgnErr(form.AdminEditUser{}), admin.EditUserPost)
|
||||
m.Post("/:userid/delete", admin.DeleteUser)
|
||||
})
|
||||
|
||||
m.Group("/orgs", func() {
|
||||
m.Get("", admin.Organizations)
|
||||
})
|
||||
|
||||
m.Group("/repos", func() {
|
||||
m.Get("", admin.Repos)
|
||||
m.Post("/delete", admin.DeleteRepo)
|
||||
})
|
||||
|
||||
m.Group("/auths", func() {
|
||||
m.Get("", admin.Authentications)
|
||||
m.Combo("/new").Get(admin.NewAuthSource).Post(bindIgnErr(form.Authentication{}), admin.NewAuthSourcePost)
|
||||
m.Combo("/:authid").Get(admin.EditAuthSource).
|
||||
Post(bindIgnErr(form.Authentication{}), admin.EditAuthSourcePost)
|
||||
m.Post("/:authid/delete", admin.DeleteAuthSource)
|
||||
})
|
||||
|
||||
m.Group("/notices", func() {
|
||||
m.Get("", admin.Notices)
|
||||
m.Post("/delete", admin.DeleteNotices)
|
||||
m.Get("/empty", admin.EmptyNotices)
|
||||
})
|
||||
}, reqAdmin)
|
||||
// ***** END: Admin *****
|
||||
|
||||
m.Group("", func() {
|
||||
m.Group("/:username", func() {
|
||||
m.Get("", user.Profile)
|
||||
m.Get("/followers", user.Followers)
|
||||
m.Get("/following", user.Following)
|
||||
m.Get("/stars", user.Stars)
|
||||
}, context.InjectParamsUser())
|
||||
|
||||
m.Get("/attachments/:uuid", func(c *context.Context) {
|
||||
attach, err := models.GetAttachmentByUUID(c.Params(":uuid"))
|
||||
if err != nil {
|
||||
c.NotFoundOrServerError("GetAttachmentByUUID", models.IsErrAttachmentNotExist, err)
|
||||
return
|
||||
} else if !com.IsFile(attach.LocalPath()) {
|
||||
c.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
fr, err := os.Open(attach.LocalPath())
|
||||
if err != nil {
|
||||
c.Handle(500, "Open", err)
|
||||
return
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
c.Header().Set("Cache-Control", "public,max-age=86400")
|
||||
fmt.Println("attach.Name:", attach.Name)
|
||||
c.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, attach.Name))
|
||||
if err = repo.ServeData(c, attach.Name, fr); err != nil {
|
||||
c.Handle(500, "ServeData", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
m.Post("/issues/attachments", repo.UploadIssueAttachment)
|
||||
m.Post("/releases/attachments", repo.UploadReleaseAttachment)
|
||||
}, ignSignIn)
|
||||
|
||||
m.Group("/:username", func() {
|
||||
m.Post("/action/:action", user.Action)
|
||||
}, reqSignIn, context.InjectParamsUser())
|
||||
|
||||
if macaron.Env == macaron.DEV {
|
||||
m.Get("/template/*", dev.TemplatePreview)
|
||||
}
|
||||
|
||||
reqRepoAdmin := context.RequireRepoAdmin()
|
||||
reqRepoWriter := context.RequireRepoWriter()
|
||||
|
||||
// ***** START: Organization *****
|
||||
m.Group("/org", func() {
|
||||
m.Group("", func() {
|
||||
m.Get("/create", org.Create)
|
||||
m.Post("/create", bindIgnErr(form.CreateOrg{}), org.CreatePost)
|
||||
}, func(c *context.Context) {
|
||||
if !c.User.CanCreateOrganization() {
|
||||
c.NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
m.Group("/:org", func() {
|
||||
m.Get("/dashboard", user.Dashboard)
|
||||
m.Get("/^:type(issues|pulls)$", user.Issues)
|
||||
m.Get("/members", org.Members)
|
||||
m.Get("/members/action/:action", org.MembersAction)
|
||||
|
||||
m.Get("/teams", org.Teams)
|
||||
}, context.OrgAssignment(true))
|
||||
|
||||
m.Group("/:org", func() {
|
||||
m.Get("/teams/:team", org.TeamMembers)
|
||||
m.Get("/teams/:team/repositories", org.TeamRepositories)
|
||||
m.Route("/teams/:team/action/:action", "GET,POST", org.TeamsAction)
|
||||
m.Route("/teams/:team/action/repo/:action", "GET,POST", org.TeamsRepoAction)
|
||||
}, context.OrgAssignment(true, false, true))
|
||||
|
||||
m.Group("/:org", func() {
|
||||
m.Get("/teams/new", org.NewTeam)
|
||||
m.Post("/teams/new", bindIgnErr(form.CreateTeam{}), org.NewTeamPost)
|
||||
m.Get("/teams/:team/edit", org.EditTeam)
|
||||
m.Post("/teams/:team/edit", bindIgnErr(form.CreateTeam{}), org.EditTeamPost)
|
||||
m.Post("/teams/:team/delete", org.DeleteTeam)
|
||||
|
||||
m.Group("/settings", func() {
|
||||
m.Combo("").Get(org.Settings).
|
||||
Post(bindIgnErr(form.UpdateOrgSetting{}), org.SettingsPost)
|
||||
m.Post("/avatar", binding.MultipartForm(form.Avatar{}), org.SettingsAvatar)
|
||||
m.Post("/avatar/delete", org.SettingsDeleteAvatar)
|
||||
|
||||
m.Group("/hooks", func() {
|
||||
m.Get("", org.Webhooks)
|
||||
m.Post("/delete", org.DeleteWebhook)
|
||||
m.Get("/:type/new", repo.WebhooksNew)
|
||||
m.Post("/gogs/new", bindIgnErr(form.NewWebhook{}), repo.WebHooksNewPost)
|
||||
m.Post("/slack/new", bindIgnErr(form.NewSlackHook{}), repo.SlackHooksNewPost)
|
||||
m.Post("/discord/new", bindIgnErr(form.NewDiscordHook{}), repo.DiscordHooksNewPost)
|
||||
m.Post("/dingtalk/new", bindIgnErr(form.NewDingtalkHook{}), repo.DingtalkHooksNewPost)
|
||||
m.Get("/:id", repo.WebHooksEdit)
|
||||
m.Post("/gogs/:id", bindIgnErr(form.NewWebhook{}), repo.WebHooksEditPost)
|
||||
m.Post("/slack/:id", bindIgnErr(form.NewSlackHook{}), repo.SlackHooksEditPost)
|
||||
m.Post("/discord/:id", bindIgnErr(form.NewDiscordHook{}), repo.DiscordHooksEditPost)
|
||||
m.Post("/dingtalk/:id", bindIgnErr(form.NewDingtalkHook{}), repo.DingtalkHooksEditPost)
|
||||
})
|
||||
|
||||
m.Route("/delete", "GET,POST", org.SettingsDelete)
|
||||
})
|
||||
|
||||
m.Route("/invitations/new", "GET,POST", org.Invitation)
|
||||
}, context.OrgAssignment(true, true))
|
||||
}, reqSignIn)
|
||||
// ***** END: Organization *****
|
||||
|
||||
// ***** START: Repository *****
|
||||
m.Group("/repo", func() {
|
||||
m.Get("/create", repo.Create)
|
||||
m.Post("/create", bindIgnErr(form.CreateRepo{}), repo.CreatePost)
|
||||
m.Get("/migrate", repo.Migrate)
|
||||
m.Post("/migrate", bindIgnErr(form.MigrateRepo{}), repo.MigratePost)
|
||||
m.Combo("/fork/:repoid").Get(repo.Fork).
|
||||
Post(bindIgnErr(form.CreateRepo{}), repo.ForkPost)
|
||||
}, reqSignIn)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Group("/settings", func() {
|
||||
m.Combo("").Get(repo.Settings).
|
||||
Post(bindIgnErr(form.RepoSetting{}), repo.SettingsPost)
|
||||
m.Combo("/avatar").Get(repo.SettingsAvatar).
|
||||
Post(binding.MultipartForm(form.Avatar{}), repo.SettingsAvatarPost)
|
||||
m.Post("/avatar/delete", repo.SettingsDeleteAvatar)
|
||||
m.Group("/collaboration", func() {
|
||||
m.Combo("").Get(repo.SettingsCollaboration).Post(repo.SettingsCollaborationPost)
|
||||
m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
|
||||
m.Post("/delete", repo.DeleteCollaboration)
|
||||
})
|
||||
m.Group("/branches", func() {
|
||||
m.Get("", repo.SettingsBranches)
|
||||
m.Post("/default_branch", repo.UpdateDefaultBranch)
|
||||
m.Combo("/*").Get(repo.SettingsProtectedBranch).
|
||||
Post(bindIgnErr(form.ProtectBranch{}), repo.SettingsProtectedBranchPost)
|
||||
}, func(c *context.Context) {
|
||||
if c.Repo.Repository.IsMirror {
|
||||
c.NotFound()
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
m.Group("/hooks", func() {
|
||||
m.Get("", repo.Webhooks)
|
||||
m.Post("/delete", repo.DeleteWebhook)
|
||||
m.Get("/:type/new", repo.WebhooksNew)
|
||||
m.Post("/gogs/new", bindIgnErr(form.NewWebhook{}), repo.WebHooksNewPost)
|
||||
m.Post("/slack/new", bindIgnErr(form.NewSlackHook{}), repo.SlackHooksNewPost)
|
||||
m.Post("/discord/new", bindIgnErr(form.NewDiscordHook{}), repo.DiscordHooksNewPost)
|
||||
m.Post("/dingtalk/new", bindIgnErr(form.NewDingtalkHook{}), repo.DingtalkHooksNewPost)
|
||||
m.Post("/gogs/:id", bindIgnErr(form.NewWebhook{}), repo.WebHooksEditPost)
|
||||
m.Post("/slack/:id", bindIgnErr(form.NewSlackHook{}), repo.SlackHooksEditPost)
|
||||
m.Post("/discord/:id", bindIgnErr(form.NewDiscordHook{}), repo.DiscordHooksEditPost)
|
||||
m.Post("/dingtalk/:id", bindIgnErr(form.NewDingtalkHook{}), repo.DingtalkHooksEditPost)
|
||||
|
||||
m.Group("/:id", func() {
|
||||
m.Get("", repo.WebHooksEdit)
|
||||
m.Post("/test", repo.TestWebhook)
|
||||
m.Post("/redelivery", repo.RedeliveryWebhook)
|
||||
})
|
||||
|
||||
m.Group("/git", func() {
|
||||
m.Get("", repo.SettingsGitHooks)
|
||||
m.Combo("/:name").Get(repo.SettingsGitHooksEdit).
|
||||
Post(repo.SettingsGitHooksEditPost)
|
||||
}, context.GitHookService())
|
||||
})
|
||||
|
||||
m.Group("/keys", func() {
|
||||
m.Combo("").Get(repo.SettingsDeployKeys).
|
||||
Post(bindIgnErr(form.AddSSHKey{}), repo.SettingsDeployKeysPost)
|
||||
m.Post("/delete", repo.DeleteDeployKey)
|
||||
})
|
||||
|
||||
}, func(c *context.Context) {
|
||||
c.Data["PageIsSettings"] = true
|
||||
})
|
||||
}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef())
|
||||
|
||||
m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/issues", repo.RetrieveLabels, repo.Issues)
|
||||
m.Get("/issues/:index", repo.ViewIssue)
|
||||
m.Get("/labels/", repo.RetrieveLabels, repo.Labels)
|
||||
m.Get("/milestones", repo.Milestones)
|
||||
}, ignSignIn, context.RepoAssignment(true))
|
||||
m.Group("/:username/:reponame", func() {
|
||||
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
|
||||
// So they can apply their own enable/disable logic on routers.
|
||||
m.Group("/issues", func() {
|
||||
m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue).
|
||||
Post(bindIgnErr(form.NewIssue{}), repo.NewIssuePost)
|
||||
|
||||
m.Group("/:index", func() {
|
||||
m.Post("/title", repo.UpdateIssueTitle)
|
||||
m.Post("/content", repo.UpdateIssueContent)
|
||||
m.Combo("/comments").Post(bindIgnErr(form.CreateComment{}), repo.NewComment)
|
||||
})
|
||||
})
|
||||
m.Group("/comments/:id", func() {
|
||||
m.Post("", repo.UpdateCommentContent)
|
||||
m.Post("/delete", repo.DeleteComment)
|
||||
})
|
||||
}, reqSignIn, context.RepoAssignment(true))
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Group("/wiki", func() {
|
||||
m.Get("/?:page", repo.Wiki)
|
||||
m.Get("/_pages", repo.WikiPages)
|
||||
}, repo.MustEnableWiki, context.RepoRef())
|
||||
}, ignSignIn, context.RepoAssignment(false, true))
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
|
||||
// So they can apply their own enable/disable logic on routers.
|
||||
m.Group("/issues", func() {
|
||||
m.Group("/:index", func() {
|
||||
m.Post("/label", repo.UpdateIssueLabel)
|
||||
m.Post("/milestone", repo.UpdateIssueMilestone)
|
||||
m.Post("/assignee", repo.UpdateIssueAssignee)
|
||||
}, reqRepoWriter)
|
||||
})
|
||||
m.Group("/labels", func() {
|
||||
m.Post("/new", bindIgnErr(form.CreateLabel{}), repo.NewLabel)
|
||||
m.Post("/edit", bindIgnErr(form.CreateLabel{}), repo.UpdateLabel)
|
||||
m.Post("/delete", repo.DeleteLabel)
|
||||
m.Post("/initialize", bindIgnErr(form.InitializeLabels{}), repo.InitializeLabels)
|
||||
}, reqRepoWriter, context.RepoRef())
|
||||
m.Group("/milestones", func() {
|
||||
m.Combo("/new").Get(repo.NewMilestone).
|
||||
Post(bindIgnErr(form.CreateMilestone{}), repo.NewMilestonePost)
|
||||
m.Get("/:id/edit", repo.EditMilestone)
|
||||
m.Post("/:id/edit", bindIgnErr(form.CreateMilestone{}), repo.EditMilestonePost)
|
||||
m.Get("/:id/:action", repo.ChangeMilestonStatus)
|
||||
m.Post("/delete", repo.DeleteMilestone)
|
||||
}, reqRepoWriter, context.RepoRef())
|
||||
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/new", repo.NewRelease)
|
||||
m.Post("/new", bindIgnErr(form.NewRelease{}), repo.NewReleasePost)
|
||||
m.Post("/delete", repo.DeleteRelease)
|
||||
m.Get("/edit/*", repo.EditRelease)
|
||||
m.Post("/edit/*", bindIgnErr(form.EditRelease{}), repo.EditReleasePost)
|
||||
}, repo.MustBeNotBare, reqRepoWriter, func(c *context.Context) {
|
||||
c.Data["PageIsViewFiles"] = true
|
||||
})
|
||||
|
||||
// FIXME: Should use c.Repo.PullRequest to unify template, currently we have inconsistent URL
|
||||
// for PR in same repository. After select branch on the page, the URL contains redundant head user name.
|
||||
// e.g. /org1/test-repo/compare/master...org1:develop
|
||||
// which should be /org1/test-repo/compare/master...develop
|
||||
m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
|
||||
Post(bindIgnErr(form.NewIssue{}), repo.CompareAndPullRequestPost)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Combo("/_edit/*").Get(repo.EditFile).
|
||||
Post(bindIgnErr(form.EditRepoFile{}), repo.EditFilePost)
|
||||
m.Combo("/_new/*").Get(repo.NewFile).
|
||||
Post(bindIgnErr(form.EditRepoFile{}), repo.NewFilePost)
|
||||
m.Post("/_preview/*", bindIgnErr(form.EditPreviewDiff{}), repo.DiffPreviewPost)
|
||||
m.Combo("/_delete/*").Get(repo.DeleteFile).
|
||||
Post(bindIgnErr(form.DeleteRepoFile{}), repo.DeleteFilePost)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Combo("/_upload/*").Get(repo.UploadFile).
|
||||
Post(bindIgnErr(form.UploadRepoFile{}), repo.UploadFilePost)
|
||||
m.Post("/upload-file", repo.UploadFileToServer)
|
||||
m.Post("/upload-remove", bindIgnErr(form.RemoveUploadFile{}), repo.RemoveUploadFileFromServer)
|
||||
}, func(c *context.Context) {
|
||||
if !setting.Repository.Upload.Enabled {
|
||||
c.NotFound()
|
||||
return
|
||||
}
|
||||
})
|
||||
}, repo.MustBeNotBare, reqRepoWriter, context.RepoRef(), func(c *context.Context) {
|
||||
if !c.Repo.CanEnableEditor() {
|
||||
c.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["PageIsViewFiles"] = true
|
||||
})
|
||||
}, reqSignIn, context.RepoAssignment())
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Group("", func() {
|
||||
m.Get("/releases", repo.MustBeNotBare, repo.Releases)
|
||||
m.Get("/pulls", repo.RetrieveLabels, repo.Pulls)
|
||||
m.Get("/pulls/:index", repo.ViewPull)
|
||||
}, context.RepoRef())
|
||||
|
||||
m.Group("/branches", func() {
|
||||
m.Get("", repo.Branches)
|
||||
m.Get("/all", repo.AllBranches)
|
||||
m.Post("/delete/*", reqSignIn, reqRepoWriter, repo.DeleteBranchPost)
|
||||
}, repo.MustBeNotBare, func(c *context.Context) {
|
||||
c.Data["PageIsViewFiles"] = true
|
||||
})
|
||||
|
||||
m.Group("/wiki", func() {
|
||||
m.Group("", func() {
|
||||
m.Combo("/_new").Get(repo.NewWiki).
|
||||
Post(bindIgnErr(form.NewWiki{}), repo.NewWikiPost)
|
||||
m.Combo("/:page/_edit").Get(repo.EditWiki).
|
||||
Post(bindIgnErr(form.NewWiki{}), repo.EditWikiPost)
|
||||
m.Post("/:page/delete", repo.DeleteWikiPagePost)
|
||||
}, reqSignIn, reqRepoWriter)
|
||||
}, repo.MustEnableWiki, context.RepoRef())
|
||||
|
||||
m.Get("/archive/*", repo.MustBeNotBare, repo.Download)
|
||||
|
||||
m.Group("/pulls/:index", func() {
|
||||
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
|
||||
m.Get("/files", context.RepoRef(), repo.ViewPullFiles)
|
||||
m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
|
||||
}, repo.MustAllowPulls)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Get("/src/*", repo.Home)
|
||||
m.Get("/raw/*", repo.SingleDownload)
|
||||
m.Get("/commits/*", repo.RefCommits)
|
||||
m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.Diff)
|
||||
m.Get("/forks", repo.Forks)
|
||||
}, repo.MustBeNotBare, context.RepoRef())
|
||||
m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.MustBeNotBare, repo.RawDiff)
|
||||
|
||||
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.MustBeNotBare, context.RepoRef(), repo.CompareDiff)
|
||||
}, ignSignIn, context.RepoAssignment())
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/stars", repo.Stars)
|
||||
m.Get("/watchers", repo.Watchers)
|
||||
}, ignSignIn, context.RepoAssignment(), context.RepoRef())
|
||||
|
||||
m.Group("/:username", func() {
|
||||
m.Get("/:reponame", ignSignIn, context.RepoAssignment(), context.RepoRef(), repo.Home)
|
||||
|
||||
m.Group("/:reponame", func() {
|
||||
m.Head("/tasks/trigger", repo.TriggerTask)
|
||||
})
|
||||
// Use the regexp to match the repository name
|
||||
// Duplicated routes to enable different ways of accessing same set of URLs,
|
||||
// e.g. with or without ".git" suffix.
|
||||
m.Group("/:reponame([\\d\\w-_\\.]+\\.git$)", func() {
|
||||
m.Get("", ignSignIn, context.RepoAssignment(), context.RepoRef(), repo.Home)
|
||||
m.Options("/*", ignSignInAndCsrf, repo.HTTPContexter(), repo.HTTP)
|
||||
m.Route("/*", "GET,POST", ignSignInAndCsrf, repo.HTTPContexter(), repo.HTTP)
|
||||
})
|
||||
m.Options("/:reponame/*", ignSignInAndCsrf, repo.HTTPContexter(), repo.HTTP)
|
||||
m.Route("/:reponame/*", "GET,POST", ignSignInAndCsrf, repo.HTTPContexter(), repo.HTTP)
|
||||
})
|
||||
// ***** END: Repository *****
|
||||
|
||||
m.Group("/api", func() {
|
||||
apiv1.RegisterRoutes(m)
|
||||
}, ignSignIn)
|
||||
|
||||
m.Group("/-", func() {
|
||||
if setting.Prometheus.Enabled {
|
||||
m.Get("/metrics", func(c *context.Context) {
|
||||
if !setting.Prometheus.EnableBasicAuth {
|
||||
return
|
||||
}
|
||||
|
||||
c.RequireBasicAuth(setting.Prometheus.BasicAuthUsername, setting.Prometheus.BasicAuthPassword)
|
||||
}, promhttp.Handler())
|
||||
}
|
||||
})
|
||||
|
||||
// robots.txt
|
||||
m.Get("/robots.txt", func(c *context.Context) {
|
||||
if setting.HasRobotsTxt {
|
||||
c.ServeFileContent(path.Join(setting.CustomPath, "robots.txt"))
|
||||
} else {
|
||||
c.NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
// Not found handler.
|
||||
m.NotFound(routes.NotFound)
|
||||
|
||||
// Flag for port number in case first time run conflict.
|
||||
if c.IsSet("port") {
|
||||
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, c.String("port"), 1)
|
||||
setting.HTTPPort = c.String("port")
|
||||
}
|
||||
|
||||
var listenAddr string
|
||||
if setting.Protocol == setting.SCHEME_UNIX_SOCKET {
|
||||
listenAddr = fmt.Sprintf("%s", setting.HTTPAddr)
|
||||
} else {
|
||||
listenAddr = fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.HTTPPort)
|
||||
}
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||
|
||||
var err error
|
||||
switch setting.Protocol {
|
||||
case setting.SCHEME_HTTP:
|
||||
err = http.ListenAndServe(listenAddr, m)
|
||||
case setting.SCHEME_HTTPS:
|
||||
var tlsMinVersion uint16
|
||||
switch setting.TLSMinVersion {
|
||||
case "SSL30":
|
||||
tlsMinVersion = tls.VersionSSL30
|
||||
case "TLS12":
|
||||
tlsMinVersion = tls.VersionTLS12
|
||||
case "TLS11":
|
||||
tlsMinVersion = tls.VersionTLS11
|
||||
case "TLS10":
|
||||
fallthrough
|
||||
default:
|
||||
tlsMinVersion = tls.VersionTLS10
|
||||
}
|
||||
server := &http.Server{Addr: listenAddr, TLSConfig: &tls.Config{
|
||||
MinVersion: tlsMinVersion,
|
||||
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||
PreferServerCipherSuites: true,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // Required for HTTP/2 support.
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
}, Handler: m}
|
||||
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
|
||||
case setting.SCHEME_FCGI:
|
||||
err = fcgi.Serve(nil, m)
|
||||
case setting.SCHEME_UNIX_SOCKET:
|
||||
os.Remove(listenAddr)
|
||||
|
||||
var listener *net.UnixListener
|
||||
listener, err = net.ListenUnix("unix", &net.UnixAddr{listenAddr, "unix"})
|
||||
if err != nil {
|
||||
break // Handle error after switch
|
||||
}
|
||||
|
||||
// FIXME: add proper implementation of signal capture on all protocols
|
||||
// execute this on SIGTERM or SIGINT: listener.Close()
|
||||
if err = os.Chmod(listenAddr, os.FileMode(setting.UnixSocketPermission)); err != nil {
|
||||
log.Fatal(4, "Failed to set permission of unix socket: %v", err)
|
||||
}
|
||||
err = http.Serve(listener, m)
|
||||
default:
|
||||
log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(4, "Failed to start server: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user