From 964e537479c497a5ba42799a1c1a7c430720e990 Mon Sep 17 00:00:00 2001 From: Gogs <gogitservice@gmail.com> Date: Sun, 23 Mar 2014 18:13:23 +0800 Subject: [PATCH] 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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�IW9|e&$CB3<IJ3}8 zFfr#5ok7V%Ar>dSt1J~Yc$O(!p^@>Gc=546ey`F^!Nj6o(R9Ln1CS*6_>cBv44rqB z9k%|kob*oq>B*yVGq$Zr#yue=XL$VvoMzb)rYX4d68WJ1L*R^XCidto-Bo?b_AoXm zFvk*2>G>+#lN}F)S2_UO`!hNn=Xxi5x<xK{*tbxJ-)f2I9l}aD2NFfhz>gI+-OZ1h z68md$tEAZCv=uX=wB)ZY9XC{T2^$Bn(7WZQD7dNUdT-8robJGhq>72jrTS%*4Pktv z?TI|op;GD~@XBIwM`(9yw#lhbXQsLbT@Co=o~n+LImf1MZlXfI%QUBo`r=R{@q=UB z(#eUC8Ah`ahH)9Uw_;)VvBK44?r;L8ET5bSx+HgAnw(0To&$wxy9Uwfx#toCj;$2? zA-(mNr*}o^YGHF=$;{mFU-VC}zrV?~+-wt{Y%9ohipZODSvRh&WTbnq)mGf;3#(aM z2t#a+s>qz4ikOysc_WKwLo?aPVzeBxbp4b6J?~=wE@3nw^~6)^EYtdp$K9s;N7v`l zCYaRm=;Y`d;83>M2mcOH-b1Enh`YnyJMVMl#GD=+;qC(myd;0{4N#F7LS-*%7hPT` zBP+w~Clu}(@H?k^Y7vVy9d(70QiA@KAI`@z%o?WC*@IG(l^VzB;HSbZ7&D@5i}Vj) zfB2~d=<!0wI&QP$K+Q?a)J1@*S{mMn`c_7xOG=?th;!LS%_VY<7k4Mb<D&O*tj5lz z{O`ixt5XWmKOYs#`YUN0x=AhYEM~<GOG+oFel?Tl@<&D}NLMS}m^r#+jO4>~;ix47 zb{5^ZG{b!`p|lMjF!(f78XXWpS&n}L9)k<6?H7}hZx>QODL6d^7lJCK)Zg`)pGs$% z$l7M0)g?4<kJs>i+m~B{oXV8h8-eauX{(g2Ja<XfGKbeHOj!4Y_6=(<KUMFk8D6(! zxuj(>*JWyz>9lMjgf69xVS3iN!ej_jlFelw=pT<I#uG_0J(<b%cJ!v#3oAW*c1$Z^ zrNlBl1=VHS+K0Bu*66v!W8|!S8>OT=c(VAbc+-*~6=seap?05Mm#4nytpgZ~c&8lB zrAN1$?gQ)&1zyC*3&q|>curQ}Pb*25utjNGq_v<6Wj9I$KD#ns`xxHVI6SFuz!n=R z-q37`#6XtzyHbmextKxl3ZMRn95Cvr;mLUWTfOR7YSAWT$@K4Sw_H%T>x;K#bEE*H z_}FbGeF$fEQk{<rnV~E!*^~oE|GCYlPnHUfC*Ss=&EZ#A5`)<>tvc&FGaPzn4bY2v zO|AHXBhEy)X9BO$1{?^0YXp<l(Bc);lI|7#zq{L%NO?M0X<ITi6&Q3Z$!T$YDnI6( zMMnRn5l;zjO4+?{z-k`XmSA!?mhmE~gkG@x-e-A43TavP1}R|8eWnTO!LRb6oWqKQ zLvg-Ct{q#>csesY+|=}b&<PPO^|H0|u{3ds`Phi|K}8*ErZ$`UGTP}EQ5I^MZ4*_) z>pF^t2?vRon8Hz9@}(b7B~JK|?j(-85*{&?-b-JUbJM!Z0y|<NQu9Plqdn`~Fv4c0 zXC!3e<~$xK)qxjKX3W^aV8<GN13j*Wt$Al7ws0<*M%LH63a8yce5M_1vOD`q9g>C_ za|qYfl#FjsfYpSSeAL;2M)1z37%uMNS;3K=!D1Bm<fu`;{XWYzD#z)F_avBl4_xLW z0@Y@PRcGfsHF_UkO3;Ka1q_%--jt(Boc)8;h<Rf9i6@arRAP1EnJPS(_<?A<{0&(Z zq8Q$mZ82PDk4q5k0gkP#5rTV1Q;s9!BiC0KQwugJ5an`a#(KAYljBZ&YU+jZn021W zy?Uj4sCeFndqXsC3s38@xb}!)cp@hpyDaHjqKTj@cNKJRnXK>gGA48CJ>z2qUZv{E z8xGj7RUpaYE;2~Zc^W8H_P|$jICFSM=wF`N!yn4!!%Ar<IyWq<$;Bl(p%e4#rFkh> zTJW<Q;ar$I(j&qt@SftMv5HPykHuh(PqP*U$;;84VZ9}$#7u6KVA6lp>uT_$BbhXR zV7nv)iP>GP{IZ=SkL(4<E_sH+{pqTaxM5=n<p~1jQvAHP<+YIxCp|Z1IC68_0J{Qz zJybc<2}KL+;bZb@Zo?tkH4ddiX(54dEX>B%^&;FYB}NFYF?V;;$zbD^uHVeNI)0I$ z{E;%uAgV{DyS%2As@%w`_w)!;VLn2nc@Te;)^AzT(r*Axf`iVV$da*$flVHp5_qc? z5lbseW;V(rd2|J(=9q(JWoN9P^vV@7y;1^)U81LcAU))m&A@jFm*vQ#7Dv3=`bbc9 z`NF-E)9FLShYryu9Tmt*LsNfoyQmOYnCQX58`gl{)iq-%JK_>9WfO8oDzwLC3^S0K zdc}J(L5I?nVRdmLY~5%I>a`Nq5Q;A{W!RW-nR)caM`dqPx*Q6eQZi5&N+(4R&H*Ia z9Kd>rFxpkQVG0jac~`+ATs!UyBr3>$Xu;c#VvvhZW!ZAap@Pg>eq%Td*tCPT;HwAu z<8q>~JCP{`uBve<OHA+SN)l9aGoUR<zC<2m>+MaYq<4p}Zn}a>!rX_>_SqLDbyQ&D zz^D4+)PH-6ZRJB26k4P8ce1FXbN@a!C49_SwInAbj#2JRs+PU^Oj{(mb(3Q+zV>m+ zFO-TCLQyeCgWT`6L_#Xc(^86(m>$Hhx$t~0l)JJ|l%4KpQJWn3qle|^)B3d(u+bVG z%JM}|*!$UhLdaJmnuKOou>x24vKVabP#joy$%;Gr8YyPSX^EMV<$epXP(Gs(eLk1L zi0kPW_C|<ak`px5QIl?Is43=hFrk{9YlmfNQ)I?cs*<{p`&>VJ$yrvzQH3+2&Qk78 zcRztW!4eq8-4@c6X4Ev8<NInnXiBzK`c{TUMd_z!kHn<}?ac5S=Bkr=P-*S#MAqHS z9u^-f#lW(s@d;a7zSGz9@n=oXOSq76hio<W_a$5*?CvG5`=R1$@?y0hg{@#7&t$&= z5sDHAYA&$FDu;_w;RKXcmSu=_T(&PMP*P-rMIuhx1#vo$vamBWf`U8Zr>Gr`xXKO? zfjBc{G-N92AkyTKrHP9=wrXpZV;4vI{}F76KZM%<vj3OO1Ogxt{?Isv|1mfXhyx1; z_YeQOWynuBz*jQlD7TgV#z8fc5*67pZ9zLpC&%j4Y!gUkn8@h#n9@_@@95s)krsOr z=E{4E;5bQVI;HCm-FrrNM)g;eJMzGwzX@`u<YC&8zeV)w?S+nGTO9<eM{uc#gR9@E z(?=%Af7dX!?Za$yy`MOi9<T6WQq~#hguj$#3ix~ws|;$2UvZvqBDh<cD=u_sl6rWq zBTd0*s<$Xc$eX%jrAe)MSX;#cVe%(&_aIBlQ7%q#%+35hBWq2=?HCww(5qvPcRI}J zi}@g+UI+tFm*4Q>np}<aM+G$_weesK(`JFyLB#P6-r?4du=;uV8KXEOG%D9I3Magv zu^^uQo%BvyW?CGDPn=z#C&5k+sEk80m;)Qrz>NMkK9?lwGTgzb<-)T6syi!}U{Z%$ zpBMmb7wPv67#^c-X|py8t$`5A+T$;ePuqa2eE7^Y=iQaQO@>2HLz<>UI9X@+p*E4% zVr{Lf2^aKA#~I4egJZoO#?9u`H^ATSu)J^vtTI#%MOeX`cAHtcWj_2Bn@a0NYF}H_ z1}cxE@7KiIZ`u-=$Lzo?&2H}cN()a;M%YpsQ<GBaqW^;2Wq>YP(B1B@KO8S<TDyfh z8PxD9*=Y$j(!O^51|U|4YmOZAEe4Y~H6-ybDh=&L97u<mFuGHvw;Lcywc^Nr5RM*# zswu%;H~k~1Os&(LYa~$L{{}ELhEiFkMy4p6pqn`g`y+ZcJ1ilevAj@gMb%wTIlVRe zPB8;C>!TJa*5Xjmc|D?<1}fX|yhDq|gCb?EslM9#Wm!_u8>IX&tw7Fz9{kG<<6?5S zX!$MOgW+uA$Jv}CvfWCImc#{Gxsyl70=v^5LN*bXamre#K1WO1*J)!1m^s*MxJhAq z?Og0{H3$3-qA=`$M2^%?L0aJ*QQkcd-KOY8;60-t>7qZaoRhKaJ(MPIf-|kavn^lT z9N(mX;e?IggEcut<Q|Z(%*{Bkc5WG(Ar0zLtb9|P>K}_NFnQMg46PA@2$bHLa(TDk zj4OeDPS;MjOO}9GX=|(Jc`XlRi8b5*^B^Q@pgvJMopU;5EVo-;>SdX>7Uabf-P^W} z&?8M7pT-^8pC7Ezmdqk6jr2X`zSZ_lb~7@NN>lQR?fB()I(hj*4c#i3FsM49kbXVX z6SI0Xd1pAdKBI;+mQb%pB%`b1ab#`BCJj;^eV4`Z)7yE~f3F}`Gy1*xo~7{jv{1?h zNq}$Sp;~6J!vm(IvaciPRnhvI`Z$tUf&bS4C8q9q`_g%^!pf8hnr8YXpk=t~I>c@1 z7qO6aLb})*qOd#G7Ty7hgVJm85{4J`El!p$jw&hX>!HQ?;dx9-r=ExhDxPsvMr8eP znHI)sw<v6W>lB+9F4fC2YVJV*7Go{OL;$Y&*|SP5FiG}<_6yxCdHC#=UPUfo5&`p< zAExeYK5uv$p0Pm+>1C)&xi<~|CW1%qX3&%3LP_Kl@{!>7A%)iRom`=k4KeO3MkMtK zYpKFWvC%ZmM7HqN{z4kNM3Go+$%BehH}@#)%P`Br`LA}m{>IEQbR-6Ss*&V%-z&VG zA(^NR*s9{yL*u0SMne=KF5?*E>M1Q)v1;JfzF`5c`jJLxSQf$!c|Tut8V3ZK5ddZe zbNI7Bi(AvNfY&t5PNiuot@f?;@SJ;cJv(28x~Rx$AJghKdJy5i5;a(9q8*QajCtuA zr9wqHfQ9%kDQ_7{Y+Rl6H=tyjr1B|rb*Ia1TN}~_(@Tcqf!~kr)!RGVrQmNNovdD) zRQvN-Dc5nXFcVEJ#;(2!rO~u8(dk2hAEREPQs-)Ac2p~iln0i^;d7dqDzjX6Z4VO= z2zDZED*c{4DtG9kIk%Ltraf~Z%?^di)7JO{M{js?5Voc6^9ywexOF2^Y=F}qmG+Ml zPs1)w9!*#F`!F#$kM-tD^qq5dWtcQEt!_lHRA4d+*bHYX#Mv6i>t6Vi^n_%^v}~6Q z6%GhqS)sLDcbdDaN|<g=4>dcAx)+Ix=hR&H>28ypxh)V)jnb0>gaf~#PTRT;x3xmP zlFwNSgkZj5<}uw{!y)%_WA5!`@nuUMfvyh0Q?Aks>t{WNeeQOhgy}fOgj%#qOc(6} z50Hk=>YOocv)?&F?NQg<Ln|tb$eQ}}UloO5=z;fnjl5?y;*~4@3Pps~dysC6O^GmK zT+TAOTQvbAK?+VbXn!v*T+zEv#&26ax6kd8>ypv%RR|v+GPT?DDM2>v6!o6`6w&y~ z>=W!vRlkCc<PVI}V9%L(CUfgA=+v%=yy>5Z9qgAm4Nw<`jm=;3|0a(jZy6L`dw3+9 ztYMz`_?_sS)6e@mD#d28&TTO7*p0?j)s>HFz$oc?Pwf90;^SYZ)x!#D1{x%CYO-oy zk^wy^HNJ;e_hx1$8b{02COD>FrbE8li>gh2%(`-3u1T20ep7JVZq`o-Vvm&VErfT* z4W?N3B0E-(upsT8g76HW_ry!k*0W^+X{)g_$t|B(se#7ViivfLvsN}(;q61Px>XD4 zHtFLEG`p-AK3cnVVsp$6G%tFTnuU_B^Ld=S90)Kb(_(&48Nob7GRqrlCv?L&U6SR= zz>7>LQa53)%glo_^rcKMltF&W=Tgk(2y#fhniz|YHlpCErQQ3KyMH$`nKg!N?~akK zV~)m?SvB@wfDUVF)}qhpElO%hLzyUT_DohA*wI#Co+@5Fu&d7@tVNvR{m7(rj5peC zgA<&g-Ax;<E%{J3)brziq%BabK(X~ETZ-5xtvfqpGvli@0dcKx(joj(iu<i-FLqo% zweU$ML3uR^gTR_@r-kP<U-arTs0NpuA<QFBjkaCZYN@~cL&*S%%o5m|F$uGRtn1d; z#S-wf8udeWGi@CqmR0EzWn|v{Ot0nmbwgD&qk24IRJsiqp^?+81%#UBCfX?a#Mf8R zSqM?q=i=qbad`Hb-`@aA#R=8xgaEVIBMlzEeR7^!_3(m)lZ<q9p*7WhdtHuDJ`m}D zda2~*Dx&jtODOm^V8YYd>9sSQ$t#spF_#mzgbFMNjp(#QVBH6Jg>53TQ@|LurW5U) zbckn2{)5T4dxuY?G_VfR0e@>NS#w7AtsSI?0#vI6U8r811>zYruaZ1t?JpHvvePqk zSMEiyh~X+pGiy37at{}?4bmoG+;dNcW5(oIhew4dy?-QivgpJzP!cEX%EV<|h{=Z1 zN;6e@?jYs$eVv&zi*F91+4$O)%(7}7**u<kLfe>auEPv0)<`}^i%*(ZIRaV+alxmD zu(3y0RI;@F$uRzlmIWxk9d%eAm`#U)V8Ce|VH|-f;b^}BjRL8|?}htrWr><aAQosa zcV-3JV(=)gTHBzf&Dyzt$F%4aZ6|j(C-*zMer>Z>e7PCn-rlUWB)^Q<7_p?~(9S|7 z41`f@w76ABnCY3~j`!#9Y>67t_DDx!>8Nn|4#!UnXttxvDP?Ge6LCa-1A>HWwoqkK z=52Dxe5N^tESLQ*wYiPA5b2#EK}UP=503}bxn2n_Q)CL_FB{nu>46guJd-6GRFQ63 zccu3E+b=eTT_vdb_L=dxQA2Nw*pR#ce_f0+a>raUb)%d}O>5Z=j&#{Js+f7*c<d3> zFfu#K2F5Ck=u*Fa@3n=RRKc@4Er4WM3#F|fS3aK_nND$~1x)v`vvdz%(<w<Wdc$to zQGb-8s-Rma?x6Z4^crkUp`4wdYAUXX1+2TzBmtpO&G^OL(#te6d;{(r^Q$HiEl!w= zcN*R-dNFg!Q#3U|ie=eXSsv;5<$zt8b##~Tbx#=}80QGsHo+}q$BxW()-qPImt{_Q zPDt)m+E|@om;r~Rj!bpR7RR;5ddmV0!(Q_+HT|%^I;8<i;y;aEjn!_oJ{%vZ<3Zgk zkYOg~pk$Y^7=n98g&?zCIB9O;df>WYa(84#4r|^KbE#Iao0*}vbFrK2V7=?I%=x%3 z+Ro=o!^L5lGctmpc*gzYTa>pr43<-eX<>JnVY;RhJY2>2w2Meh3AF30igAawov*lG z4f<#>7!{Ez*C`Qfn%gZ^KAWPib&vV<lGPw`B|Mi>aoas5hAzpn3R+9hIt1iu^Yyqx z*TT0BJ+r#<6n{?km|mw)w@`szkS<XuoV8O)fgDST4PCVsD|F;M^~fNd=%2Wu;5WBT zk{&U2bQuubu?~K75T|i3UQc%nW=LuNDm8ioNY$d=qj<X;9ksJ)P1;@AATXb!P<M+Y z&~0C;nf+;4TDDtkpgi@#h}oV~=F4TNyk(L^JV5m``plo$rq1prtgq}l;QZ2)j3D(J zb`C1X{;f%E6%h!{W^lFzOigLy={rnL`*ECN|Cq_>D7T<se#>|F4uoR)1AP8R5B;N$ z1^^H><p`tGY+^d)IbvXgpnhjjp!RdJ3j)~nxyMuI(o0N@^t?|^QXCJFu^*d7B8a-5 zz%b!DZo7G4MX)RiyR<*`Znup|z|dU!2*2|98r`J_ir<sZS)!R`2(-Zic{5zQhrtqK zXp`CbPmj(Il<IacaH&6Lg7Eo1?hC&G?*VT=aE%1%f5Wx^t3COFYlwn|js%K<1qlrc z2meQRg1GPD7dreF>W=l|!1RxVLI;jNGCGiWn+&6-KC#3dO1mL#<c+yMP&!2Wo&S+h zO9|G#dW!9|P=y7FuCAh;5l<bK)Dom}IOXUCmmD2@qE_voM?ioRhsXSn<t<{8bo=E9 z`WGX*0i1u|@&+9WLYw0n(B$C$PQ2L4S@{x_?`M|uS>198o!SH1rOK~D^?pTA84%EX z0imP~@<Zw9={e-lf2;v3BeER<YA`A06uHWj@IZ{=PWzKbW^y$IuD*kozsydJa0bef zzdDgioPVC!7~K$#WWkDCzgTKWAHCZ%mN*cgTS`l(Ok~;{*^z)Wj8K4DwyY0)01kkW zE)9Y-qxucLG-|>jrK21gf~tcxL{+$vv49RXxh?7ellMRRlK=b(00;UXA;W)~+5dsR zhy*_X`2IfxJn+9g`k%f8@tA2v&}Dct=JQ>p+=}Lb(W*$1uW>+w`W|t0^4U)Jr)?CJ z6eAu)1Sd{`a`5k{734XZx|Z+pXx!4@nNaI1qWd}p!k&1vzLE_mT9)n}kks@yhcFFn zc_fv9+fh#jCGP+Oc<`#O*K|V7`7XoO+sC|=@4-sNAo(V7_(`#;S!mt+E}c5H)93Di zi_GonBSJGxX0UVcSFlg<Bz)t6#owKmtWpeKcf2B{6ELUf1Xmi0RLx6kV@SOc(4+2k zGvz`h-Uc0pSaQ`=Zxt6k!iXdYf(XzYq#_v(eY9#z0t1X!GBYjHm}OL+kBB2xOg1Y+ ztyX<RqmSc`4}OOp<j_;2Ta9{27qMFtodM%rxKo~B5w0twR;DL>yD7~GJL7_8TXc9a zsi->UR1TF`Ow@f)!ojj1Ka(Q<2zY)%_D7uj^Y))O83+mRe`$t+gZqX5%Q7J9reOXt zDycZ;b=**;oa(-y^?p3D@?@LHE@-^zXaV$9FVE@jp6-ateue(B`6z3zxHg3O26QFj zN?W_$0tVu5?iu@o)cgPbFu<sx`NGg}@M~KAnS}Zbij9AJ`ibuE&R!1`(R2@5@^7CE z75F(F+kWAwoLUgB-nM)LF!EnkXAV>Tuy-q7zddCA2%e}`FcAblcR39o7Ond<XA1?j z4*mRizxwYeLBTfWhs3>&=Dz{dWU=eO7kY2-OvAJ^Sm;JMh9m+KsUi#(>h8j9i6lDQ zVFPL{;od`yjxHjO&-{QwjXJeC51aW3-JyyI%3<NIi$<?EWHNu)<)udkN755JRSbyw zTeQ8`AsWBmflwyq!z1UbU)7YYKP~@ii3)l2ST@_R)rqhG<oD&jho1eb)0+}22Xnia zXqqZOKI7m<kndZ)y5iGLw!pr>;D;vQF4w*R+$tani*Yb^<ey;mksd?@nKSy3+*1$8 z7`xT#FT1Z|^<p+vPm!3>plCbf05zDmq|=X50lCrx*CJ2z>cdjr5#joq$rHKMYnBF3 zLB%$}g(SbyYOmrGM=7mPJJH55xwDb*ZBYBO68xOAYRpsnJmACJVM_OU>y{(RlXQj} zY=L(!Ps)uWP=4pdA)*9NoLijq=nkKe!u&*G2EoCP0B+`7x4-J}(v$-)msGu2ltKgy zm=W7wYUcOj@$zPZt`NZXW3oVOMqE+Ut4|DSS(J<c$-iuuj!oo6!Inyi5;wy@vJ9vL z43fC!j)jHU;da5TCc4<gq&{hbr9usG2;t$KAR3Yl!e%giRCAK;kKYhLak=nr0$e7% z5u<j*l{pB=m+ky}T8rj(-Y;M}58@v!oA{ys=g-j!+7kpa!=3wEW_KG}+ts$QjCw`U z<!rR)mKQz|XzSlyg4+U8UDVQ7OzC*YLaGFg;Qp$oWFcVS@%nyAp$w?!P6cWcnU95u zES)3~hIrS8#iB8C`vLtphA4U}+sjzOkO|`Xn|>aK1PlxnR0%M_fXb6PJ+x4{3Yl<q zDetUAI6QJ8VJm%X3>tyJ(iC)#Y>@#+@W4!@e`4^=&-SK-Y^<dy>1P%RJ$?*<!M2@$ z?LDSRaz7FXo|kp-r;-c47-eIup7)s@f-uksKenO*xKZ`bu!*`FmOn>N^c6t90qJgk zu2_?N4PSaK-rmQmZCeQ;)hE|wOyeD_x*Ai3I!h^8sPE`m0L3IkWQ>v7nE90WiUh#N zSm<Nt783e^zs5JO%#TrI@v4fK-q1xTB8DV2J37II+cU)!7Cb;9Bo5$#T(u$d1lxXF zZ*piUfZp`{gY`l|j^ehn3@Y2<F1SSQJ6ueWQ0^`JBdXQa+#fYgI)vc0lmFfOuy?Sr zYiw9tS==2<HU3qIOEoXj-xEqX6Is>FT9OF#%u}KbA<?3M^$7BJ@eVSBRxNUb5ZU*y zNQDt@^;mUL$?}B$EOX}+2#b60D(`@a`Akj{q=lyaMo1Mc=ze0O2p~q`J`Q3qXdxs) zGeVm2ML=NJgo3&?=_4DtSUDg9dN?%CKj?B0AR^#>b4$7sh&v@zLXa-TGZ<0Td<BR0 zfe1)t$d5g=!@km;dNHb3Gy@|SfNI`_8)DonylMJ`3-JOi%|rhN_}vqK1T(nEzmiF? zs@8D-wy?7KX?<eaVw_eFY%EqiIS6a|!0LE!^cd&_dh|WSc&FJZL+YXr;u?BX^72x^ zp`FqYAaRc;vJK*x>HQIgYXFqj5XM}UNjUN&<K<r^eFO4Kpd<lI?7o=DwTmuLxVl*R z4HXf#2}CqnWLqwWL|plcK!kLqfye47fiOq1F@<ngLI=N3)E?BOQ47O&s0tw`rs*jZ z*IgK@c%`KPRDwQU(?S>PwF?-Lws@^N4|~mHi}s&mI&KQLRMG{1I){;&6<-FB%V|2= zP!bQd5T=jfV|n{9jce>|aD#LIH7P}`9kHY&Ey+oe?mOU0e54oB6~wcSNa6V!OJ69V zk)+t6frKv#&=z7|!iZ}#PSe%I2W|tbM47thYyk!EYZhu|O7L}c1`E*`@lo=jS_G&$ z#3aa%Lete^!;skMcHUvF44SJ$CLDz(nLx(4p+?zYp+7Btu^O9lK8whs)%EGpQLCi# ze2T(@e5U4xEtq8yN!IZ7#(5~0CoJTf(#?NPu`q3Ov!W`QWZS8e59v#CFLvIni2=&v zH^YQ@yB#L~*!!Z58=#8X{yiof@9D_tV%Mevp08dKI0Xa>5n(VAFH*X(3bWBmq@XEq zKIa!fL`1+x*OY1N`Q#;mLr`7uaEnJcPJl76^|CazCS6qFIq`$=R-=WTs^QxWtE>c6 zvfUqTDm2}%n+cmKDDj#L;znfI-NJ$kxzbMdoUQ4?(r6*@@g>?p-MHDTg{C39A(B}% zQ<h{^&2VB=#x_y%GJ*%`TKiU2D**_*J_giM&g>Sw`*wqEy$684xM(5HrP_wmy?yJt zJ%5aTm>%Ekz)ROPMq~x<5kw-kR2FM^PdnO9acDs?)G52lKZRpn5<^g3G@lnafXvWx zVFHFi>v|<%WOI&vI{?cF?D?xHsk=L~06;;S$Qq)L%ZaE-MB|p@<er!;DPlqFGU(up zT|T`Ytu>kep1b_>#sSM@fdB`{2A~=QvaJBQVD0D>yE`P(PtsD@wG>2=AAm|=l_Wf! zM?eY?6d6_a`M5<ijZ>M3R-H_5FM%odjG~GTYwV|f>o!Y`sTEe)jSSi2gX)MujQs`> ze~!0e8zx;IQ+6cB?O)#J8WsN$j7Zc}uuIVqO93sC5@6Q@UiF+3f^o~A6U1t^H+ZV= zRK<o)BJbV~ZdiZ{L2Gw?fg@klZpmKCU?c@Y!CF}Yx|_w2HGDRqn8^YFW6&dD?&Lo+ zTxaF<QUl7pSXcqu@ZA;!0ztAp$kUpLCB8;mhkt%M*g`Q}0zupS8jUfI_gU|B?G{s$ z(Eup~`&&@094HB8M~y{XqB<ULIXxe{a7TxsZaz0^B^;#<#WEEFg_S+{n1@qqG5w@J zu8)^BI+{l0h_)$jShydYB%vjyJ|N;n(3z%2RXt-<zWW>tIb{+;a|kr;AkC5J0QZ2$ zeGvLgUFR^6nRpkHQKzP}Y+8wNSx#4&y-!8btLR7+9H5qCOE}G-bd6zFC9bz$`*++8 zF$0VK7Z^2zHy-clFUSZ3w+oa)rGt?nCPt;A5NzrIRI%WUJ)BU|t#~CvD8L2E3Bo9d zl{g?l)>@96K)usZ;b7-06}7c%DT>5x@z#V|;QbI$>#c)apc=YwiqQwwMGzK=FNXLi zx_YxPD_)WCw$W%NqBI5z#RmBBbUbz~KN57rqoMS#iE$3@J$G_;6OI{uf#d^A)y3*V z`}+l)<yyawJ*2ZszWvQ+kPb}X&Dc>CvW2T7#q)E4ZCHyjT%B~YM&~tFSgNUOCWgfd zJ{l@XGB7k|;H%K@d<t(-)F;SWK~T<s8XX)Bb;thSxyYR&jxMD!;H_M|wS@16k3VH0 zg8GN+NI(7Ac6z{V19W3&y4AV-8ERn9)i%wJSXnxX5o;vRxT@IRNQg}oco%HUTykkW z)x2flAM%9@^89;oZNN@blb<PY5R%P7u;RZL4i%vJipH*PpHc)1ZMgwct_8<`E7g;T zM$HH_g$5q^D_|I@fdw{$$wskCy5PxI1nEX+Q~^LN=RvGjh$P4NiGG26p~Dm{b~flH zaFmd|6IbVzlnV5vhv%$mwnB@saD+&RV&k(&;yt27qVO5j%LpkA&)<Nrh-CGRP~Ehr zX~4GEZnT>c-4L~buC=EJS5P~gh(1T@I<^}AuE4VoN{_)`I?r|_D?Ffpt|#IGDg;@3 z0036CM&AI6q<tAhD3Dg^4}PFXpo2Gd6Amd$Rx@%x_s%NbnoeWVk1T6qmT(FVp`I%g z%`e<Vl!BAkj;%$rM@Q_!QXo@^(gMWN-D*U_SW<xKP}Qyb2Ut^PY1ebC8i!lSVwJ~a z<`X_dIVB}BqN0|zFM6=s6O{)c8HLj2lf!1XE2L0nuIw9N1^PO@_ugS##=S+A4l_Wh z{X?M(w6b{~D*?&#SRlZ*{u+W>maz%!HYy<uWEjC0;3r+JgoTC-00RLHFA%`7kX!Bf z@W{VhUXS7skg}zMFiXCI4?`m(gKYAK%=f;SB@$^tud9gAg}x?=0KRf!*HL@M&=Rh> ze7DwNPO$4Y)IzvelVRzIY$>P6Dx26BV?*X*X_!=~f(em}+5^F^2lYr*>#}fQajnc^ zLu0JVO&|c3EC;Iyo){+Czaa_P!$~1T7jeOj(w)n8O6bv!+b4G@*L*Z0J-%84uOYJ| zS$Ydy!Pi6)WTEzy%x4zsp{Xst0z)*RFGNNaKLiRtWLKOb+D8mawLLEFjOH&`0$dBm z3Hyu+hqCcHq|l{?;H*NkGE}sv_RL9AFUQYCFg;_Dmj!sIX#F$GEim5aO^YY_vb<BU z>UukCG<mrALp_gX!Nu-fBIUM=`fKfXLAe%!?lMFtX+x*>%qJG&<p2b#NKs8l6**FA z;Y_(IVZ?)g-6d~HGcN4!5|%h~7cABzjZL*E7ab`i&ux~bft)!*hg?QvX0}8V@cGZL zh<xg8F2Du(D6gq(zJUb{H6LdT_OSw&Lh5-J%k<vMKY5ZOE|)y;pMNUrL^c(sxOG_w zz&Msw)FgbYHbha5>*1B?e=<k~baY%^TXu7ek@gCA?|uWsFb{<#y#Ba@X8od*84L+2 zz(Fwe<~NL_1Va;3d{Ki0g-{u`neIR)R9~HN2PKFpmeCefNk(WvJ_POight}~RiBQ; zu#t@B!+4H2y#kqSk;sy7y}NBl4E^~Hc%nx56DtcITR8MN1d4rK5j#~WVgEHKEMB~a zzYB=xhv!fCK1Cp~@Vvc$1R@I9&Esfuur;wD2EslSOPHrFjR5j%Hu0f$a^q;U=6xi! zZ(o}5o`#y|@`C2$^CF`~b`?M^I+})q!m`5ZJ?12Yx@bD$afssmQyCpwG$_`766)ba zlv^Z=D(&#^M2p-i?6jG*(n=ILnWR!JD7|iK3Z}3diU1y1q97H<Ne+7Ti5M$hjvxlG z@D>PFrS(%gVm6O7yc<@M<cwHZ|LJEyeV<JI81kJcIh7C7R;~pU%*{p{i18VJ4qah* zy1*yma|$;2-vo#XdCRf-N(h4{LoATUZ1!V%!89UVG6=l_Cm`uXm>(q9R4xYP#qN6& zP4F1aMjVEXipHb_g7LyIVo&M1@dc1|(9euIRcMuTF0&?Mocpl_R8>HBpujD~Zw6z) zLiDs_eN4IJ+uocuX}7%(93vveEY?lg+zLI!7Q5@#_}=;$j<F3wd>TS1y)z-hc%qWD zy(Ozd5oEek{FZl6@M+@=+a`uu!#k#Nc0KfdPA{DkI{+5MXw%4`HGU?`|9=8y0-F8o zGwTW7ida^f;M2?#d7gX(eLgZKblS{cr~*&C0#~-&sB22NjTAavcrD&J-K>aU`qBDv z+@i9Gh^R-@9Ovy}5~B901J_)~V{HYGK!(CDC9o_AG!h2q<2tzr2(Si#d$m(}rOpTl zReY?*$%)g?yfA^uhKw_!<GC=mNSiRQ-&l1LOK&oe0^(#Rzq}AH4qQPj&lCRue-11b z23i803di-39hg{mE5s=Ln3^Pa*;jqzIwFvT`&C+gFnKWUTfgkv`pXE_P&E>HsgjSx z;?VEKn7{rOu0%YN=fNU<f;t)>&QsS~IPB{Ce(*S3wY7l$DTyLAf~gJ-TlvEXO-<?- z;Dq~b6?E%ts#E*Oj6gHt<|1rn-;4t*ecT|iM)y7_4eyu*cl|Qt!}Ff-+1w-+8)Bb6 zv1i`h41!1JPzvO0udEQ8gtNMx6`!n+g@F-KUbg<GB4g7{C-`!C5nwcQpP%;?qAU^$ zKezcYgb-tj>-aE37aSt#c;KfCtSufSclCwI!*lTQy2&*Ft9A?P^T!L*ZX1aExtz-d zR4pHx&b}FIXG(P<<*rNs`q2nYe((V551p=WlU;8v$%AHT-AvMEz;(=aN&Yc5Y&-Df z_E<M=CF*`y1Bjs#H}#U_kVAAv{PfA$iH7(oqMzWzo8z=WSLFy~g$1zaf8!vO5^RL8 z^9>_%S^$myu}s|1qzkXA;Etr=Bva2p$5?!OaM$Ix>khAR1^)mCz2;KR#slB06>@=| r6z@+r6fu?f1kjq3ue#n=PlQ9d2Cc+uB=6|S6?Uy3jL;>f{B!@=bPBAW 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)