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:
Unknwon
2019-10-24 01:51:46 -07:00
committed by GitHub
parent 613139e7be
commit 01c8df01ec
178 changed files with 1609 additions and 1608 deletions

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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,
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}