2014-02-19 13:50:53 +04:00
// 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.
2014-05-02 05:21:46 +04:00
package cmd
2014-02-19 13:50:53 +04:00
import (
"fmt"
2014-02-19 23:49:08 +04:00
"html/template"
2014-05-28 08:06:31 +04:00
"io/ioutil"
2014-02-19 13:50:53 +04:00
"net/http"
2014-04-16 04:01:20 +04:00
"os"
2014-05-26 04:11:25 +04:00
"path"
2014-02-19 13:50:53 +04:00
2014-07-26 08:24:27 +04:00
"github.com/Unknwon/macaron"
2014-02-19 13:50:53 +04:00
"github.com/codegangsta/cli"
2014-08-01 01:25:34 +04:00
"github.com/macaron-contrib/cache"
"github.com/macaron-contrib/captcha"
"github.com/macaron-contrib/csrf"
2014-07-26 08:24:27 +04:00
"github.com/macaron-contrib/i18n"
"github.com/macaron-contrib/session"
2014-08-07 01:21:24 +04:00
"github.com/macaron-contrib/toolbox"
2014-02-19 13:50:53 +04:00
2014-08-07 01:21:24 +04:00
"github.com/gogits/gogs/models"
2014-03-06 11:21:44 +04:00
"github.com/gogits/gogs/modules/auth"
2014-05-05 21:08:01 +04:00
"github.com/gogits/gogs/modules/auth/apiv1"
2014-03-23 14:13:23 +04:00
"github.com/gogits/gogs/modules/avatar"
2014-03-06 11:21:44 +04:00
"github.com/gogits/gogs/modules/base"
2014-03-08 02:22:15 +04:00
"github.com/gogits/gogs/modules/log"
2014-03-15 15:01:50 +04:00
"github.com/gogits/gogs/modules/middleware"
2014-05-05 10:42:52 +04:00
"github.com/gogits/gogs/modules/middleware/binding"
2014-05-26 04:11:25 +04:00
"github.com/gogits/gogs/modules/setting"
2014-02-19 13:50:53 +04:00
"github.com/gogits/gogs/routers"
2014-03-20 15:50:26 +04:00
"github.com/gogits/gogs/routers/admin"
2014-03-29 18:01:52 +04:00
"github.com/gogits/gogs/routers/api/v1"
2014-03-19 20:50:44 +04:00
"github.com/gogits/gogs/routers/dev"
2014-07-26 10:28:04 +04:00
"github.com/gogits/gogs/routers/org"
2014-02-20 06:45:43 +04:00
"github.com/gogits/gogs/routers/repo"
2014-02-19 13:50:53 +04:00
"github.com/gogits/gogs/routers/user"
)
var CmdWeb = cli . Command {
Name : "web" ,
2014-05-02 05:21:46 +04:00
Usage : "Start Gogs web server" ,
2014-05-05 08:55:17 +04:00
Description : ` Gogs web server is the only thing you need to run ,
2014-03-24 15:36:38 +04:00
and it takes care of all the other things for you ` ,
2014-02-19 13:50:53 +04:00
Action : runWeb ,
2014-03-13 10:09:36 +04:00
Flags : [ ] cli . Flag { } ,
2014-02-19 13:50:53 +04:00
}
2014-08-24 17:09:05 +04:00
// checkVersion checks if binary matches the version of templates files.
2014-05-26 04:57:01 +04:00
func checkVersion ( ) {
2014-07-26 08:24:27 +04:00
data , err := ioutil . ReadFile ( path . Join ( setting . StaticRootPath , "templates/.VERSION" ) )
2014-05-26 04:57:01 +04:00
if err != nil {
2014-07-26 08:24:27 +04:00
log . Fatal ( 4 , "Fail to read 'templates/.VERSION': %v" , err )
2014-05-26 04:57:01 +04:00
}
if string ( data ) != setting . AppVer {
2014-07-26 08:24:27 +04:00
log . Fatal ( 4 , "Binary and template file version does not match, did you forget to recompile?" )
2014-05-26 04:57:01 +04:00
}
}
2014-07-26 08:24:27 +04:00
// newMacaron initializes Macaron instance.
func newMacaron ( ) * macaron . Macaron {
m := macaron . New ( )
m . Use ( macaron . Logger ( ) )
m . Use ( macaron . Recovery ( ) )
m . Use ( macaron . Static ( "public" ,
macaron . StaticOptions {
SkipLogging : ! setting . DisableRouterLog ,
} ,
) )
2014-08-07 01:21:24 +04:00
if setting . EnableGzip {
m . Use ( macaron . Gzip ( ) )
}
2014-07-26 08:24:27 +04:00
m . Use ( macaron . Renderer ( macaron . RenderOptions {
Directory : path . Join ( setting . StaticRootPath , "templates" ) ,
Funcs : [ ] template . FuncMap { base . TemplateFuncs } ,
IndentJSON : macaron . Env != macaron . PROD ,
} ) )
2014-08-07 01:21:24 +04:00
m . Use ( i18n . I18n ( i18n . Options {
2014-07-26 08:24:27 +04:00
Langs : setting . Langs ,
Names : setting . Names ,
Redirect : true ,
} ) )
2014-08-01 01:25:34 +04:00
m . Use ( cache . Cacher ( cache . Options {
Adapter : setting . CacheAdapter ,
Interval : setting . CacheInternal ,
Conn : setting . CacheConn ,
} ) )
m . Use ( captcha . Captchaer ( ) )
2014-07-26 08:24:27 +04:00
m . Use ( session . Sessioner ( session . Options {
Provider : setting . SessionProvider ,
Config : * setting . SessionConfig ,
} ) )
2014-08-01 01:25:34 +04:00
m . Use ( csrf . Generate ( csrf . Options {
Secret : setting . SecretKey ,
SetCookie : true ,
} ) )
2014-08-07 01:21:24 +04:00
m . Use ( toolbox . Toolboxer ( m , toolbox . Options {
HealthCheckFuncs : [ ] * toolbox . HealthCheckFuncDesc {
& toolbox . HealthCheckFuncDesc {
Desc : "Database connection" ,
Func : models . Ping ,
} ,
} ,
} ) )
2014-08-07 14:40:05 +04:00
m . Use ( middleware . Contexter ( ) )
2014-07-26 08:24:27 +04:00
return m
2014-03-19 13:31:38 +04:00
}
2014-02-19 13:50:53 +04:00
func runWeb ( * cli . Context ) {
2014-03-30 01:50:51 +04:00
routers . GlobalInit ( )
2014-05-30 14:34:24 +04:00
checkVersion ( )
2014-02-19 13:50:53 +04:00
2014-07-26 08:24:27 +04:00
m := newMacaron ( )
2014-03-15 15:01:50 +04:00
2014-03-22 21:44:02 +04:00
reqSignIn := middleware . Toggle ( & middleware . ToggleOptions { SignInRequire : true } )
2014-05-26 04:11:25 +04:00
ignSignIn := middleware . Toggle ( & middleware . ToggleOptions { SignInRequire : setting . Service . RequireSignInView } )
2014-04-16 12:12:31 +04:00
ignSignInAndCsrf := middleware . Toggle ( & middleware . ToggleOptions { DisableCsrf : true } )
2014-03-22 21:44:02 +04:00
reqSignOut := middleware . Toggle ( & middleware . ToggleOptions { SignOutRequire : true } )
2014-05-05 10:42:52 +04:00
bindIgnErr := binding . BindIgnErr
2014-04-10 22:37:43 +04:00
2014-02-19 13:50:53 +04:00
// Routers.
2014-03-24 19:58:46 +04:00
m . Get ( "/" , ignSignIn , routers . Home )
2014-07-26 10:28:04 +04:00
m . Get ( "/install" , bindIgnErr ( auth . InstallForm { } ) , routers . Install )
m . Post ( "/install" , bindIgnErr ( auth . InstallForm { } ) , routers . InstallPost )
2014-07-26 08:24:27 +04:00
m . Group ( "" , func ( r * macaron . Router ) {
2014-06-06 06:07:35 +04:00
r . Get ( "/pulls" , user . Pulls )
2014-07-27 07:53:16 +04:00
r . Get ( "/issues" , user . Issues )
2014-06-06 06:07:35 +04:00
} , reqSignIn )
2014-03-29 18:01:52 +04:00
2014-07-26 08:24:27 +04:00
// API routers.
m . Group ( "/api" , func ( _ * macaron . Router ) {
m . Group ( "/v1" , func ( r * macaron . Router ) {
2014-05-22 05:37:13 +04:00
// Miscellaneous.
r . Post ( "/markdown" , bindIgnErr ( apiv1 . MarkdownForm { } ) , v1 . Markdown )
r . Post ( "/markdown/raw" , v1 . MarkdownRaw )
// Users.
2014-08-29 13:31:53 +04:00
m . Group ( "/users" , func ( r * macaron . Router ) {
r . Get ( "/search" , v1 . SearchUsers )
} )
2014-07-12 08:55:19 +04:00
// Repositories.
2014-08-29 13:31:53 +04:00
m . Group ( "/repos" , func ( r * macaron . Router ) {
r . Get ( "/search" , v1 . SearchRepos )
r . Post ( "/migrate" , bindIgnErr ( auth . MigrateRepoForm { } ) , v1 . Migrate )
} )
2014-05-22 05:37:13 +04:00
2014-08-01 01:25:34 +04:00
r . Any ( "/*" , func ( ctx * middleware . Context ) {
2014-05-22 05:37:13 +04:00
ctx . JSON ( 404 , & base . ApiJsonErr { "Not Found" , v1 . DOC_URL } )
} )
2014-05-05 21:08:01 +04:00
} )
2014-03-29 18:01:52 +04:00
} )
2014-03-23 00:00:46 +04:00
2014-07-26 08:24:27 +04:00
// User routers.
m . Group ( "/user" , func ( r * macaron . Router ) {
2014-05-06 00:21:43 +04:00
r . Get ( "/login" , user . SignIn )
2014-07-26 08:24:27 +04:00
r . Post ( "/login" , bindIgnErr ( auth . SignInForm { } ) , user . SignInPost )
2014-04-12 19:19:17 +04:00
r . Get ( "/login/:name" , user . SocialSignIn )
2014-04-11 00:36:50 +04:00
r . Get ( "/sign_up" , user . SignUp )
r . Post ( "/sign_up" , bindIgnErr ( auth . RegisterForm { } ) , user . SignUpPost )
r . Get ( "/reset_password" , user . ResetPasswd )
r . Post ( "/reset_password" , user . ResetPasswdPost )
2014-03-23 00:00:46 +04:00
} , reqSignOut )
2014-08-14 10:12:21 +04:00
m . Group ( "/user/settings" , func ( r * macaron . Router ) {
r . Get ( "" , user . Settings )
r . Post ( "" , bindIgnErr ( auth . UpdateProfileForm { } ) , user . SettingsPost )
r . Get ( "/password" , user . SettingsPassword )
r . Post ( "/password" , bindIgnErr ( auth . ChangePasswordForm { } ) , user . SettingsPasswordPost )
r . Get ( "/ssh" , user . SettingsSSHKeys )
r . Post ( "/ssh" , bindIgnErr ( auth . AddSSHKeyForm { } ) , user . SettingsSSHKeysPost )
r . Get ( "/social" , user . SettingsSocial )
r . Route ( "/delete" , "GET,POST" , user . SettingsDelete )
2014-03-23 00:00:46 +04:00
} , reqSignIn )
2014-07-26 08:24:27 +04:00
m . Group ( "/user" , func ( r * macaron . Router ) {
// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
2014-04-18 20:12:10 +04:00
r . Any ( "/activate" , user . Activate )
2014-04-13 04:35:35 +04:00
r . Get ( "/email2user" , user . Email2User )
2014-04-11 02:09:57 +04:00
r . Get ( "/forget_password" , user . ForgotPasswd )
r . Post ( "/forget_password" , user . ForgotPasswdPost )
2014-04-19 05:14:35 +04:00
r . Get ( "/logout" , user . SignOut )
2014-03-23 00:00:46 +04:00
} )
2014-03-10 12:54:52 +04:00
2014-07-26 08:24:27 +04:00
m . Get ( "/user/:username" , ignSignIn , user . Profile ) // TODO: Legacy
2014-08-01 01:25:34 +04:00
// Gravatar service.
avt := avatar . CacheServer ( "public/img/avatar/" , "public/img/avatar_default.jpg" )
os . MkdirAll ( "public/img/avatar/" , os . ModePerm )
m . Get ( "/avatar/:hash" , avt . ServeHTTP )
2014-03-08 02:08:21 +04:00
2014-03-22 21:44:02 +04:00
adminReq := middleware . Toggle ( & middleware . ToggleOptions { SignInRequire : true , AdminRequire : true } )
m . Get ( "/admin" , adminReq , admin . Dashboard )
2014-07-26 08:24:27 +04:00
m . Group ( "/admin" , func ( r * macaron . Router ) {
2014-03-23 00:00:46 +04:00
r . Get ( "/users" , admin . Users )
r . Get ( "/repos" , admin . Repositories )
2014-05-03 06:48:14 +04:00
r . Get ( "/auths" , admin . Auths )
2014-06-13 21:01:52 +04:00
r . Get ( "/config" , admin . Config )
r . Get ( "/monitor" , admin . Monitor )
2014-03-23 00:00:46 +04:00
} , adminReq )
2014-07-26 08:24:27 +04:00
m . Group ( "/admin/users" , func ( r * macaron . Router ) {
2014-04-11 02:09:57 +04:00
r . Get ( "/new" , admin . NewUser )
r . Post ( "/new" , bindIgnErr ( auth . RegisterForm { } ) , admin . NewUserPost )
r . Get ( "/:userid" , admin . EditUser )
r . Post ( "/:userid" , bindIgnErr ( auth . AdminEditUserForm { } ) , admin . EditUserPost )
2014-08-29 11:32:52 +04:00
r . Post ( "/:userid/delete" , admin . DeleteUser )
2014-03-23 00:00:46 +04:00
} , adminReq )
2014-07-26 08:24:27 +04:00
m . Group ( "/admin/auths" , func ( r * macaron . Router ) {
2014-05-03 06:48:14 +04:00
r . Get ( "/new" , admin . NewAuthSource )
r . Post ( "/new" , bindIgnErr ( auth . AuthenticationForm { } ) , admin . NewAuthSourcePost )
r . Get ( "/:authid" , admin . EditAuthSource )
2014-05-05 12:40:25 +04:00
r . Post ( "/:authid" , bindIgnErr ( auth . AuthenticationForm { } ) , admin . EditAuthSourcePost )
2014-05-03 06:48:14 +04:00
r . Get ( "/:authid/delete" , admin . DeleteAuthSource )
} , adminReq )
2014-07-12 08:55:19 +04:00
m . Get ( "/:username" , ignSignIn , user . Profile )
2014-07-26 08:24:27 +04:00
if macaron . Env == macaron . DEV {
2014-08-01 01:25:34 +04:00
m . Get ( "/template/*" , dev . TemplatePreview )
2014-03-27 19:37:33 +04:00
}
2014-07-26 10:28:04 +04:00
reqTrueOwner := middleware . RequireTrueOwner ( )
// Organization routers.
m . Group ( "/org" , func ( r * macaron . Router ) {
2014-07-27 07:53:16 +04:00
r . Get ( "/create" , org . Create )
r . Post ( "/create" , bindIgnErr ( auth . CreateOrgForm { } ) , org . CreatePost )
2014-07-26 10:28:04 +04:00
2014-08-14 10:12:21 +04:00
m . Group ( "/:org" , func ( r * macaron . Router ) {
r . Get ( "/dashboard" , user . Dashboard )
r . Get ( "/members" , org . Members )
2014-08-15 14:29:41 +04:00
r . Get ( "/members/action/:action" , org . MembersAction )
2014-07-26 10:28:04 +04:00
2014-08-14 10:12:21 +04:00
r . Get ( "/teams" , org . Teams )
2014-08-24 17:09:05 +04:00
r . Get ( "/teams/:team" , org . TeamMembers )
2014-08-26 14:11:15 +04:00
r . Get ( "/teams/:team/repositories" , org . TeamRepositories )
2014-08-16 12:21:17 +04:00
r . Get ( "/teams/:team/action/:action" , org . TeamsAction )
2014-08-26 14:11:15 +04:00
r . Get ( "/teams/:team/action/repo/:action" , org . TeamsRepoAction )
2014-08-14 10:12:21 +04:00
} , middleware . OrgAssignment ( true , true ) )
2014-07-26 10:28:04 +04:00
2014-08-14 10:12:21 +04:00
m . Group ( "/:org" , func ( r * macaron . Router ) {
r . Get ( "/teams/new" , org . NewTeam )
r . Post ( "/teams/new" , bindIgnErr ( auth . CreateTeamForm { } ) , org . NewTeamPost )
r . Get ( "/teams/:team/edit" , org . EditTeam )
2014-08-24 17:09:05 +04:00
r . Post ( "/teams/:team/edit" , bindIgnErr ( auth . CreateTeamForm { } ) , org . EditTeamPost )
r . Post ( "/teams/:team/delete" , org . DeleteTeam )
2014-08-14 10:12:21 +04:00
m . Group ( "/settings" , func ( r * macaron . Router ) {
r . Get ( "" , org . Settings )
r . Post ( "" , bindIgnErr ( auth . UpdateOrgSettingForm { } ) , org . SettingsPost )
r . Route ( "/delete" , "GET,POST" , org . SettingsDelete )
} )
2014-08-15 14:29:41 +04:00
r . Route ( "/invitations/new" , "GET,POST" , org . Invitation )
2014-08-16 12:21:17 +04:00
} , middleware . OrgAssignment ( true , true , true ) )
2014-07-26 10:28:04 +04:00
} , reqSignIn )
2014-08-27 12:39:36 +04:00
m . Group ( "/org" , func ( r * macaron . Router ) {
r . Get ( "/:org" , org . Home )
} , middleware . OrgAssignment ( true ) )
2014-07-26 10:28:04 +04:00
2014-07-27 07:53:16 +04:00
// Repository routers.
m . Group ( "/repo" , func ( r * macaron . Router ) {
r . Get ( "/create" , repo . Create )
r . Post ( "/create" , bindIgnErr ( auth . CreateRepoForm { } ) , repo . CreatePost )
r . Get ( "/migrate" , repo . Migrate )
r . Post ( "/migrate" , bindIgnErr ( auth . MigrateRepoForm { } ) , repo . MigratePost )
} , reqSignIn )
2014-07-26 10:28:04 +04:00
m . Group ( "/:username/:reponame" , func ( r * macaron . Router ) {
2014-08-02 21:47:33 +04:00
r . Get ( "/settings" , repo . Settings )
r . Post ( "/settings" , bindIgnErr ( auth . RepoSettingForm { } ) , repo . SettingsPost )
2014-07-26 10:28:04 +04:00
m . Group ( "/settings" , func ( r * macaron . Router ) {
2014-08-07 14:40:05 +04:00
r . Route ( "/collaboration" , "GET,POST" , repo . SettingsCollaboration )
2014-08-09 21:29:51 +04:00
r . Get ( "/hooks" , repo . Webhooks )
2014-08-10 02:40:10 +04:00
r . Get ( "/hooks/new" , repo . WebHooksNew )
r . Post ( "/hooks/new" , bindIgnErr ( auth . NewWebhookForm { } ) , repo . WebHooksNewPost )
2014-07-26 10:28:04 +04:00
r . Get ( "/hooks/:id" , repo . WebHooksEdit )
r . Post ( "/hooks/:id" , bindIgnErr ( auth . NewWebhookForm { } ) , repo . WebHooksEditPost )
} )
} , reqSignIn , middleware . RepoAssignment ( true ) , reqTrueOwner )
m . Group ( "/:username/:reponame" , func ( r * macaron . Router ) {
2014-08-11 07:11:18 +04:00
r . Get ( "/action/:action" , repo . Action )
2014-07-26 10:28:04 +04:00
m . Group ( "/issues" , func ( r * macaron . Router ) {
r . Get ( "/new" , repo . CreateIssue )
r . Post ( "/new" , bindIgnErr ( auth . CreateIssueForm { } ) , repo . CreateIssuePost )
r . Post ( "/:index" , bindIgnErr ( auth . CreateIssueForm { } ) , repo . UpdateIssue )
r . Post ( "/:index/label" , repo . UpdateIssueLabel )
r . Post ( "/:index/milestone" , repo . UpdateIssueMilestone )
r . Post ( "/:index/assignee" , repo . UpdateAssignee )
r . Get ( "/:index/attachment/:id" , repo . IssueGetAttachment )
r . Post ( "/labels/new" , bindIgnErr ( auth . CreateLabelForm { } ) , repo . NewLabel )
r . Post ( "/labels/edit" , bindIgnErr ( auth . CreateLabelForm { } ) , repo . UpdateLabel )
r . Post ( "/labels/delete" , repo . DeleteLabel )
r . Get ( "/milestones" , repo . Milestones )
r . Get ( "/milestones/new" , repo . NewMilestone )
r . Post ( "/milestones/new" , bindIgnErr ( auth . CreateMilestoneForm { } ) , repo . NewMilestonePost )
r . Get ( "/milestones/:index/edit" , repo . UpdateMilestone )
r . Post ( "/milestones/:index/edit" , bindIgnErr ( auth . CreateMilestoneForm { } ) , repo . UpdateMilestonePost )
r . Get ( "/milestones/:index/:action" , repo . UpdateMilestone )
} )
r . Post ( "/comment/:action" , repo . Comment )
r . Get ( "/releases/new" , repo . NewRelease )
r . Get ( "/releases/edit/:tagname" , repo . EditRelease )
} , reqSignIn , middleware . RepoAssignment ( true ) )
m . Group ( "/:username/:reponame" , func ( r * macaron . Router ) {
r . Post ( "/releases/new" , bindIgnErr ( auth . NewReleaseForm { } ) , repo . NewReleasePost )
r . Post ( "/releases/edit/:tagname" , bindIgnErr ( auth . EditReleaseForm { } ) , repo . EditReleasePost )
} , reqSignIn , middleware . RepoAssignment ( true , true ) )
m . Group ( "/:username/:reponame" , func ( r * macaron . Router ) {
r . Get ( "/issues" , repo . Issues )
r . Get ( "/issues/:index" , repo . ViewIssue )
r . Get ( "/pulls" , repo . Pulls )
r . Get ( "/branches" , repo . Branches )
} , ignSignIn , middleware . RepoAssignment ( true ) )
2014-07-26 08:24:27 +04:00
m . Group ( "/:username/:reponame" , func ( r * macaron . Router ) {
r . Get ( "/src/:branchname" , repo . Home )
r . Get ( "/src/:branchname/*" , repo . Home )
2014-07-26 10:28:04 +04:00
r . Get ( "/raw/:branchname/*" , repo . SingleDownload )
r . Get ( "/commits/:branchname" , repo . Commits )
r . Get ( "/commits/:branchname/search" , repo . SearchCommits )
r . Get ( "/commits/:branchname/*" , repo . FileHistory )
r . Get ( "/commit/:branchname" , repo . Diff )
r . Get ( "/commit/:branchname/*" , repo . Diff )
r . Get ( "/releases" , repo . Releases )
2014-07-26 08:24:27 +04:00
r . Get ( "/archive/*.*" , repo . Download )
2014-03-30 09:30:17 +04:00
} , ignSignIn , middleware . RepoAssignment ( true , true ) )
2014-03-23 00:00:46 +04:00
2014-07-26 08:24:27 +04:00
m . Group ( "/:username" , func ( r * macaron . Router ) {
r . Get ( "/:reponame" , middleware . RepoAssignment ( true , true , true ) , repo . Home )
m . Group ( "/:reponame" , func ( r * macaron . Router ) {
r . Any ( "/*" , repo . Http )
} )
2014-04-10 18:12:32 +04:00
} , ignSignInAndCsrf )
2014-03-21 20:48:26 +04:00
2014-03-23 14:27:01 +04:00
// Not found handler.
2014-03-23 09:48:01 +04:00
m . NotFound ( routers . NotFound )
2014-05-26 04:11:25 +04:00
var err error
listenAddr := fmt . Sprintf ( "%s:%s" , setting . HttpAddr , setting . HttpPort )
log . Info ( "Listen: %v://%s" , setting . Protocol , listenAddr )
switch setting . Protocol {
case setting . HTTP :
err = http . ListenAndServe ( listenAddr , m )
case setting . HTTPS :
err = http . ListenAndServeTLS ( listenAddr , setting . CertFile , setting . KeyFile , m )
default :
2014-07-26 08:24:27 +04:00
log . Fatal ( 4 , "Invalid protocol: %s" , setting . Protocol )
2014-05-26 04:11:25 +04:00
}
if err != nil {
2014-07-26 08:24:27 +04:00
log . Fatal ( 4 , "Fail to start server: %v" , err )
2014-03-18 17:58:58 +04:00
}
2014-02-19 13:50:53 +04:00
}