From d725c41b56e072122faf539d5a14d36c18f5c312 Mon Sep 17 00:00:00 2001 From: skyblue Date: Fri, 21 Mar 2014 22:35:43 +0800 Subject: [PATCH 1/6] add CR in C++ gitignore And try if I can commit. --- conf/gitignore/C++ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/gitignore/C++ b/conf/gitignore/C++ index 5a1b6ec43..620d3dc8a 100644 --- a/conf/gitignore/C++ +++ b/conf/gitignore/C++ @@ -10,4 +10,4 @@ # Compiled Static libraries *.lai *.la -*.a \ No newline at end of file +*.a From 8e47ae21024bc35a82215e16f1e586f94ae622c9 Mon Sep 17 00:00:00 2001 From: skyblue Date: Sun, 23 Mar 2014 12:24:09 +0800 Subject: [PATCH 2/6] add avatar inorder to view code on github --- modules/avatar/avatar.go | 136 ++++++++++++++++++++++++++++++++++ modules/avatar/avatar_test.go | 35 +++++++++ 2 files changed, 171 insertions(+) create mode 100644 modules/avatar/avatar.go create mode 100644 modules/avatar/avatar_test.go diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go new file mode 100644 index 000000000..93f842eae --- /dev/null +++ b/modules/avatar/avatar.go @@ -0,0 +1,136 @@ +package avatar + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" + "log" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + "sync" + "time" +) + +var gravatar = "http://www.gravatar.com/avatar" + +// hash email to md5 string +func HashEmail(email string) string { + h := md5.New() + h.Write([]byte(strings.ToLower(email))) + return hex.EncodeToString(h.Sum(nil)) +} + +type Avatar struct { + Hash string + cacheDir string // image save dir + reqParams string + imagePath string +} + +func New(hash string, cacheDir string) *Avatar { + return &Avatar{ + Hash: hash, + cacheDir: cacheDir, + reqParams: url.Values{ + "d": {"retro"}, + "size": {"200"}, + "r": {"pg"}}.Encode(), + imagePath: filepath.Join(cacheDir, hash+".jpg"), + } +} + +// get image from gravatar.com +func (this *Avatar) Update() { + thunder.Fetch(gravatar+"/"+this.Hash+"?"+this.reqParams, + this.Hash+".jpg") +} + +func (this *Avatar) UpdateTimeout(timeout time.Duration) { + select { + case <-time.After(timeout): + log.Println("timeout") + case <-thunder.GoFetch(gravatar+"/"+this.Hash+"?"+this.reqParams, + this.Hash+".jpg"): + } +} + +var thunder = &Thunder{QueueSize: 10} + +type Thunder struct { + QueueSize int // download queue size + q chan *thunderTask + once sync.Once +} + +func (t *Thunder) init() { + if t.QueueSize < 1 { + t.QueueSize = 1 + } + t.q = make(chan *thunderTask, t.QueueSize) + for i := 0; i < t.QueueSize; i++ { + go func() { + for { + task := <-t.q + task.Fetch() + } + }() + } +} + +func (t *Thunder) Fetch(url string, saveFile string) error { + t.once.Do(t.init) + task := &thunderTask{ + Url: url, + SaveFile: saveFile, + } + task.Add(1) + t.q <- task + task.Wait() + return task.err +} + +func (t *Thunder) GoFetch(url, saveFile string) chan error { + c := make(chan error) + go func() { + c <- t.Fetch(url, saveFile) + }() + return c +} + +// thunder download +type thunderTask struct { + Url string + SaveFile string + sync.WaitGroup + err error +} + +func (this *thunderTask) Fetch() { + this.err = this.fetch() + this.Done() +} + +func (this *thunderTask) fetch() error { + resp, err := http.Get(this.Url) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + return fmt.Errorf("status code: %d", resp.StatusCode) + } + fd, err := os.Create(this.SaveFile) + if err != nil { + return err + } + defer fd.Close() + _, err = io.Copy(fd, resp.Body) + if err != nil { + return err + } + return nil +} diff --git a/modules/avatar/avatar_test.go b/modules/avatar/avatar_test.go new file mode 100644 index 000000000..49f8f91f3 --- /dev/null +++ b/modules/avatar/avatar_test.go @@ -0,0 +1,35 @@ +package avatar + +import ( + "log" + "strconv" + "testing" + "time" +) + +func TestFetch(t *testing.T) { + hash := HashEmail("ssx205@gmail.com") + avatar := New(hash, "./") + //avatar.Update() + avatar.UpdateTimeout(time.Millisecond * 200) + time.Sleep(5 * time.Second) +} + +func TestFetchMany(t *testing.T) { + log.Println("start") + var n = 50 + ch := make(chan bool, n) + for i := 0; i < n; i++ { + go func(i int) { + hash := HashEmail(strconv.Itoa(i) + "ssx205@gmail.com") + avatar := New(hash, "./") + avatar.Update() + log.Println("finish", hash) + ch <- true + }(i) + } + for i := 0; i < n; i++ { + <-ch + } + log.Println("end") +} From 79604f553f45af658a884544187b00fb9fa3169c Mon Sep 17 00:00:00 2001 From: skyblue Date: Sun, 23 Mar 2014 15:55:27 +0800 Subject: [PATCH 3/6] fix download part problem, add png support --- modules/avatar/avatar.go | 179 +++++++++++++++++++++++++++++++++++---- 1 file changed, 162 insertions(+), 17 deletions(-) diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index 93f842eae..55d1e13d9 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -3,7 +3,11 @@ package avatar import ( "crypto/md5" "encoding/hex" + "errors" "fmt" + "image" + "image/jpeg" + "image/png" "io" "log" "net/http" @@ -13,9 +17,14 @@ import ( "strings" "sync" "time" + + "github.com/nfnt/resize" ) -var gravatar = "http://www.gravatar.com/avatar" +var ( + gravatar = "http://www.gravatar.com/avatar" + defaultImagePath = "./default.jpg" +) // hash email to md5 string func HashEmail(email string) string { @@ -25,37 +34,145 @@ func HashEmail(email string) string { } type Avatar struct { - Hash string - cacheDir string // image save dir - reqParams string - imagePath string + Hash string + cacheDir string // image save dir + reqParams string + imagePath string + expireDuration time.Duration } func New(hash string, cacheDir string) *Avatar { return &Avatar{ - Hash: hash, - cacheDir: cacheDir, + Hash: hash, + cacheDir: cacheDir, + expireDuration: time.Minute * 10, reqParams: url.Values{ "d": {"retro"}, "size": {"200"}, "r": {"pg"}}.Encode(), - imagePath: filepath.Join(cacheDir, hash+".jpg"), + imagePath: filepath.Join(cacheDir, hash+".image"), //maybe png or jpeg } } +func (this *Avatar) InCache() bool { + fileInfo, err := os.Stat(this.imagePath) + return err == nil && fileInfo.Mode().IsRegular() +} + +func (this *Avatar) Modtime() (modtime time.Time, err error) { + fileInfo, err := os.Stat(this.imagePath) + if err != nil { + return + } + return fileInfo.ModTime(), nil +} + +func (this *Avatar) Expired() bool { + if !this.InCache() { + return true + } + fileInfo, err := os.Stat(this.imagePath) + return err != nil || time.Since(fileInfo.ModTime()) > this.expireDuration +} + +// default image format: jpeg +func (this *Avatar) Encode(wr io.Writer, size int) (err error) { + var img image.Image + decodeImageFile := func(file string) (img image.Image, err error) { + fd, err := os.Open(file) + if err != nil { + return + } + defer fd.Close() + img, err = jpeg.Decode(fd) + if err != nil { + fd.Seek(0, os.SEEK_SET) + img, err = png.Decode(fd) + } + return + } + imgPath := this.imagePath + if !this.InCache() { + imgPath = defaultImagePath + } + img, err = decodeImageFile(imgPath) + if err != nil { + return + } + m := resize.Resize(uint(size), 0, img, resize.Lanczos3) + return jpeg.Encode(wr, m, nil) +} + // get image from gravatar.com func (this *Avatar) Update() { thunder.Fetch(gravatar+"/"+this.Hash+"?"+this.reqParams, - this.Hash+".jpg") + this.imagePath) } -func (this *Avatar) UpdateTimeout(timeout time.Duration) { +func (this *Avatar) UpdateTimeout(timeout time.Duration) error { + var err error select { case <-time.After(timeout): - log.Println("timeout") - case <-thunder.GoFetch(gravatar+"/"+this.Hash+"?"+this.reqParams, - this.Hash+".jpg"): + err = errors.New("get gravatar image timeout") + case err = <-thunder.GoFetch(gravatar+"/"+this.Hash+"?"+this.reqParams, + this.imagePath): } + return err +} + +func init() { + log.SetFlags(log.Lshortfile | log.LstdFlags) +} + +// http.Handle("/avatar/", avatar.HttpHandler("./cache")) +func HttpHandler(cacheDir string) func(w http.ResponseWriter, r *http.Request) { + MustInt := func(r *http.Request, defaultValue int, keys ...string) int { + var v int + for _, k := range keys { + if _, err := fmt.Sscanf(r.FormValue(k), "%d", &v); err == nil { + defaultValue = v + } + } + return defaultValue + } + + return func(w http.ResponseWriter, r *http.Request) { + urlPath := r.URL.Path + hash := urlPath[strings.LastIndex(urlPath, "/")+1:] + hash = HashEmail(hash) + size := MustInt(r, 80, "s", "size") // size = 80*80 + + avatar := New(hash, cacheDir) + if avatar.Expired() { + err := avatar.UpdateTimeout(time.Millisecond * 500) + if err != nil { + log.Println(err) + } + } + if modtime, err := avatar.Modtime(); err == nil { + etag := fmt.Sprintf("size(%d)", size) + if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) && etag == r.Header.Get("If-None-Match") { + h := w.Header() + delete(h, "Content-Type") + delete(h, "Content-Length") + w.WriteHeader(http.StatusNotModified) + return + } + w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) + w.Header().Set("ETag", etag) + } + w.Header().Set("Content-Type", "image/jpeg") + err := avatar.Encode(w, size) + if err != nil { + log.Println(err) + w.WriteHeader(500) + } + } +} + +func init() { + http.HandleFunc("/", HttpHandler("./")) + log.Fatal(http.ListenAndServe(":8001", nil)) } var thunder = &Thunder{QueueSize: 10} @@ -114,8 +231,17 @@ func (this *thunderTask) Fetch() { this.Done() } +var client = &http.Client{} + func (this *thunderTask) fetch() error { - resp, err := http.Get(this.Url) + log.Println("thunder, fetch", this.Url) + req, _ := http.NewRequest("GET", this.Url, nil) + req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") + req.Header.Set("Accept-Encoding", "gzip,deflate,sdch") + req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8") + req.Header.Set("Cache-Control", "no-cache") + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36") + resp, err := client.Do(req) if err != nil { return err } @@ -123,14 +249,33 @@ func (this *thunderTask) fetch() error { if resp.StatusCode != 200 { return fmt.Errorf("status code: %d", resp.StatusCode) } - fd, err := os.Create(this.SaveFile) + + /* + log.Println("headers:", resp.Header) + switch resp.Header.Get("Content-Type") { + case "image/jpeg": + this.SaveFile += ".jpeg" + case "image/png": + this.SaveFile += ".png" + } + */ + /* + imgType := resp.Header.Get("Content-Type") + if imgType != "image/jpeg" && imgType != "image/png" { + return errors.New("not png or jpeg") + } + */ + + tmpFile := this.SaveFile + ".part" // mv to destination when finished + fd, err := os.Create(tmpFile) if err != nil { return err } - defer fd.Close() _, err = io.Copy(fd, resp.Body) + fd.Close() if err != nil { + os.Remove(tmpFile) return err } - return nil + return os.Rename(tmpFile, this.SaveFile) } From 964e537479c497a5ba42799a1c1a7c430720e990 Mon Sep 17 00:00:00 2001 From: Gogs Date: Sun, 23 Mar 2014 18:13:23 +0800 Subject: [PATCH 4/6] append route to web --- conf/app.ini | 8 +- models/user.go | 2 +- modules/avatar/avatar.go | 137 +++++++++++++++++++--------------- modules/avatar/avatar_test.go | 41 +++++++--- modules/base/tool.go | 2 +- public/img/avatar/default.jpg | Bin 0 -> 17379 bytes web.go | 5 +- 7 files changed, 118 insertions(+), 77 deletions(-) create mode 100644 public/img/avatar/default.jpg diff --git a/conf/app.ini b/conf/app.ini index ecb0d2511..160aef0ff 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -7,7 +7,7 @@ RUN_USER = lunny RUN_MODE = dev [repository] -ROOT = /Users/%(RUN_USER)s/git/gogs-repositories +ROOT = /home/work/%(RUN_USER)s/git/gogs-repositories LANG_IGNS = Google Go|C|C++|Python|Ruby|C Sharp LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|Artistic License 2.0|BSD (3-Clause) License @@ -15,7 +15,7 @@ LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|Artistic License 2.0| DOMAIN = localhost ROOT_URL = http://%(DOMAIN)s:%(HTTP_PORT)s/ HTTP_ADDR = -HTTP_PORT = 3000 +HTTP_PORT = 8002 [database] ; Either "mysql", "postgres" or "sqlite3"(binary release only), it's your choice @@ -23,7 +23,7 @@ DB_TYPE = mysql HOST = NAME = gogs USER = root -PASSWD = +PASSWD = toor ; For "postgres" only, either "disable", "require" or "verify-full" SSL_MODE = disable ; For "sqlite3" only @@ -120,4 +120,4 @@ HOST = USER = PASSWD = ; Receivers, can be one or more, e.g. ["1@example.com","2@example.com"] -RECEIVERS = \ No newline at end of file +RECEIVERS = diff --git a/models/user.go b/models/user.go index 3c1109128..cedf34249 100644 --- a/models/user.go +++ b/models/user.go @@ -72,7 +72,7 @@ func (user *User) HomeLink() string { // AvatarLink returns the user gravatar link. func (user *User) AvatarLink() string { - return "http://1.gravatar.com/avatar/" + user.Avatar + return "/avatar/" + user.Avatar } // NewGitSig generates and returns the signature of given user. diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index 55d1e13d9..1a18d8a7e 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -1,3 +1,8 @@ +// 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. + +// for www.gravatar.com image cache package avatar import ( @@ -22,11 +27,17 @@ import ( ) var ( - gravatar = "http://www.gravatar.com/avatar" - defaultImagePath = "./default.jpg" + gravatar = "http://www.gravatar.com/avatar" ) +func debug(a ...interface{}) { + if true { + log.Println(a...) + } +} + // hash email to md5 string +// keep this func in order to make this package indenpent func HashEmail(email string) string { h := md5.New() h.Write([]byte(strings.ToLower(email))) @@ -35,6 +46,7 @@ func HashEmail(email string) string { type Avatar struct { Hash string + AlterImage string // image path cacheDir string // image save dir reqParams string imagePath string @@ -54,7 +66,7 @@ func New(hash string, cacheDir string) *Avatar { } } -func (this *Avatar) InCache() bool { +func (this *Avatar) HasCache() bool { fileInfo, err := os.Stat(this.imagePath) return err == nil && fileInfo.Mode().IsRegular() } @@ -68,11 +80,8 @@ func (this *Avatar) Modtime() (modtime time.Time, err error) { } func (this *Avatar) Expired() bool { - if !this.InCache() { - return true - } - fileInfo, err := os.Stat(this.imagePath) - return err != nil || time.Since(fileInfo.ModTime()) > this.expireDuration + modtime, err := this.Modtime() + return err != nil || time.Since(modtime) > this.expireDuration } // default image format: jpeg @@ -92,8 +101,11 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) { return } imgPath := this.imagePath - if !this.InCache() { - imgPath = defaultImagePath + if !this.HasCache() { + if this.AlterImage == "" { + return errors.New("request image failed, and no alt image offered") + } + imgPath = this.AlterImage } img, err = decodeImageFile(imgPath) if err != nil { @@ -120,61 +132,66 @@ func (this *Avatar) UpdateTimeout(timeout time.Duration) error { return err } -func init() { - log.SetFlags(log.Lshortfile | log.LstdFlags) +type avatarHandler struct { + cacheDir string + altImage string +} + +func (this *avatarHandler) mustInt(r *http.Request, defaultValue int, keys ...string) int { + var v int + for _, k := range keys { + if _, err := fmt.Sscanf(r.FormValue(k), "%d", &v); err == nil { + defaultValue = v + } + } + return defaultValue +} + +func (this *avatarHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + urlPath := r.URL.Path + hash := urlPath[strings.LastIndex(urlPath, "/")+1:] + //hash = HashEmail(hash) + size := this.mustInt(r, 80, "s", "size") // size = 80*80 + + avatar := New(hash, this.cacheDir) + avatar.AlterImage = this.altImage + if avatar.Expired() { + err := avatar.UpdateTimeout(time.Millisecond * 500) + if err != nil { + debug(err) + //log.Trace("avatar update error: %v", err) + } + } + if modtime, err := avatar.Modtime(); err == nil { + etag := fmt.Sprintf("size(%d)", size) + if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) && etag == r.Header.Get("If-None-Match") { + h := w.Header() + delete(h, "Content-Type") + delete(h, "Content-Length") + w.WriteHeader(http.StatusNotModified) + return + } + w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) + w.Header().Set("ETag", etag) + } + w.Header().Set("Content-Type", "image/jpeg") + err := avatar.Encode(w, size) + if err != nil { + //log.Warn("avatar encode error: %v", err) // will panic when err != nil + debug(err) + w.WriteHeader(500) + } } // http.Handle("/avatar/", avatar.HttpHandler("./cache")) -func HttpHandler(cacheDir string) func(w http.ResponseWriter, r *http.Request) { - MustInt := func(r *http.Request, defaultValue int, keys ...string) int { - var v int - for _, k := range keys { - if _, err := fmt.Sscanf(r.FormValue(k), "%d", &v); err == nil { - defaultValue = v - } - } - return defaultValue - } - - return func(w http.ResponseWriter, r *http.Request) { - urlPath := r.URL.Path - hash := urlPath[strings.LastIndex(urlPath, "/")+1:] - hash = HashEmail(hash) - size := MustInt(r, 80, "s", "size") // size = 80*80 - - avatar := New(hash, cacheDir) - if avatar.Expired() { - err := avatar.UpdateTimeout(time.Millisecond * 500) - if err != nil { - log.Println(err) - } - } - if modtime, err := avatar.Modtime(); err == nil { - etag := fmt.Sprintf("size(%d)", size) - if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) && etag == r.Header.Get("If-None-Match") { - h := w.Header() - delete(h, "Content-Type") - delete(h, "Content-Length") - w.WriteHeader(http.StatusNotModified) - return - } - w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) - w.Header().Set("ETag", etag) - } - w.Header().Set("Content-Type", "image/jpeg") - err := avatar.Encode(w, size) - if err != nil { - log.Println(err) - w.WriteHeader(500) - } +func HttpHandler(cacheDir string, defaultImgPath string) http.Handler { + return &avatarHandler{ + cacheDir: cacheDir, + altImage: defaultImgPath, } } -func init() { - http.HandleFunc("/", HttpHandler("./")) - log.Fatal(http.ListenAndServe(":8001", nil)) -} - +// thunder downloader var thunder = &Thunder{QueueSize: 10} type Thunder struct { @@ -234,7 +251,7 @@ func (this *thunderTask) Fetch() { var client = &http.Client{} func (this *thunderTask) fetch() error { - log.Println("thunder, fetch", this.Url) + //log.Println("thunder, fetch", this.Url) req, _ := http.NewRequest("GET", this.Url, nil) req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") req.Header.Set("Accept-Encoding", "gzip,deflate,sdch") diff --git a/modules/avatar/avatar_test.go b/modules/avatar/avatar_test.go index 49f8f91f3..a337959c6 100644 --- a/modules/avatar/avatar_test.go +++ b/modules/avatar/avatar_test.go @@ -1,29 +1,41 @@ -package avatar +// 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 avatar_test import ( "log" + "os" "strconv" "testing" "time" + + "github.com/gogits/gogs/modules/avatar" ) +const TMPDIR = "test-avatar" + func TestFetch(t *testing.T) { - hash := HashEmail("ssx205@gmail.com") - avatar := New(hash, "./") - //avatar.Update() - avatar.UpdateTimeout(time.Millisecond * 200) - time.Sleep(5 * time.Second) + os.Mkdir(TMPDIR, 0755) + defer os.RemoveAll(TMPDIR) + + hash := avatar.HashEmail("ssx205@gmail.com") + a := avatar.New(hash, TMPDIR) + a.UpdateTimeout(time.Millisecond * 200) } func TestFetchMany(t *testing.T) { + os.Mkdir(TMPDIR, 0755) + defer os.RemoveAll(TMPDIR) + log.Println("start") - var n = 50 + var n = 5 ch := make(chan bool, n) for i := 0; i < n; i++ { go func(i int) { - hash := HashEmail(strconv.Itoa(i) + "ssx205@gmail.com") - avatar := New(hash, "./") - avatar.Update() + hash := avatar.HashEmail(strconv.Itoa(i) + "ssx205@gmail.com") + a := avatar.New(hash, TMPDIR) + a.Update() log.Println("finish", hash) ch <- true }(i) @@ -33,3 +45,12 @@ func TestFetchMany(t *testing.T) { } log.Println("end") } + +// cat +// wget http://www.artsjournal.com/artfulmanager/wp/wp-content/uploads/2013/12/200x200xmirror_cat.jpg.pagespeed.ic.GOZSv6v1_H.jpg -O default.jpg +/* +func TestHttp(t *testing.T) { + http.Handle("/", avatar.HttpHandler("./", "default.jpg")) + http.ListenAndServe(":8001", nil) +} +*/ diff --git a/modules/base/tool.go b/modules/base/tool.go index 8fabb8c53..8d0d38216 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -98,7 +98,7 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string // AvatarLink returns avatar link by given e-mail. func AvatarLink(email string) string { - return "http://1.gravatar.com/avatar/" + EncodeMd5(email) + return "/avatar/" + EncodeMd5(email) } // Seconds-based time units diff --git a/public/img/avatar/default.jpg b/public/img/avatar/default.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c5a698da91f00405ca92b87252def7a5425ecd4b GIT binary patch literal 17379 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�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 literal 0 HcmV?d00001 diff --git a/web.go b/web.go index bb316a672..637ee7ce9 100644 --- a/web.go +++ b/web.go @@ -18,6 +18,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" + "github.com/gogits/gogs/modules/avatar" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/mailer" @@ -114,6 +115,9 @@ func runWeb(*cli.Context) { m.Get("/help", routers.Help) + avatarHandler := avatar.HttpHandler("public/img/avatar", "public/img/avatar/default.jpg") + m.Get("/avatar/:hash", avatarHandler.ServeHTTP) + adminReq := middleware.AdminRequire() m.Get("/admin", reqSignIn, adminReq, admin.Dashboard) m.Get("/admin/users", reqSignIn, adminReq, admin.Users) @@ -136,7 +140,6 @@ func runWeb(*cli.Context) { ignSignIn, middleware.RepoAssignment(true), repo.Single) m.Get("/:username/:reponame/commit/:commitid/**", ignSignIn, middleware.RepoAssignment(true), repo.Single) m.Get("/:username/:reponame/commit/:commitid", ignSignIn, middleware.RepoAssignment(true), repo.Single) - m.Get("/:username/:reponame", ignSignIn, middleware.RepoAssignment(true), repo.Single) m.Any("/:username/:reponame/**", ignSignIn, repo.Http) From 0d39c18b6ad715a68144d3d9e7f9ea3893f07d4f Mon Sep 17 00:00:00 2001 From: Gogs Date: Sun, 23 Mar 2014 18:19:24 +0800 Subject: [PATCH 5/6] rollback conf/app.ini --- conf/app.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/app.ini b/conf/app.ini index 160aef0ff..ecb0d2511 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -7,7 +7,7 @@ RUN_USER = lunny RUN_MODE = dev [repository] -ROOT = /home/work/%(RUN_USER)s/git/gogs-repositories +ROOT = /Users/%(RUN_USER)s/git/gogs-repositories LANG_IGNS = Google Go|C|C++|Python|Ruby|C Sharp LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|Artistic License 2.0|BSD (3-Clause) License @@ -15,7 +15,7 @@ LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|Artistic License 2.0| DOMAIN = localhost ROOT_URL = http://%(DOMAIN)s:%(HTTP_PORT)s/ HTTP_ADDR = -HTTP_PORT = 8002 +HTTP_PORT = 3000 [database] ; Either "mysql", "postgres" or "sqlite3"(binary release only), it's your choice @@ -23,7 +23,7 @@ DB_TYPE = mysql HOST = NAME = gogs USER = root -PASSWD = toor +PASSWD = ; For "postgres" only, either "disable", "require" or "verify-full" SSL_MODE = disable ; For "sqlite3" only @@ -120,4 +120,4 @@ HOST = USER = PASSWD = ; Receivers, can be one or more, e.g. ["1@example.com","2@example.com"] -RECEIVERS = +RECEIVERS = \ No newline at end of file From b9302749ddc0e0a10911a83bf80e165b792c8c1e Mon Sep 17 00:00:00 2001 From: skyblue Date: Sun, 23 Mar 2014 22:19:19 +0800 Subject: [PATCH 6/6] update default image --- public/img/avatar/default.jpg | Bin 17379 -> 6951 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/img/avatar/default.jpg b/public/img/avatar/default.jpg index c5a698da91f00405ca92b87252def7a5425ecd4b..728ec5af6c197ae10062f55331348756f9e52fc6 100644 GIT binary patch literal 6951 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�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