cleaner check for mkdir errors
This commit is contained in:
@@ -238,3 +238,4 @@ _all calls require auth and admin_
|
||||
- User can vote for the comment multiple times but only to change his/her vote. Double-voting not allowed.
|
||||
- User can edit comments in 5 mins window after creation.
|
||||
- User ID prefixed by oauth provider name in order to avoid collisions and potential abuse.
|
||||
- All avatars cached locally in order to prevent rate limiters from google/github/facebook.
|
||||
|
||||
@@ -32,9 +32,9 @@ type Provider struct {
|
||||
Endpoint oauth2.Endpoint
|
||||
Scopes []string
|
||||
MapUser func(userData, []byte) store.User
|
||||
AvatarProxy *avatar.Proxy
|
||||
|
||||
conf *oauth2.Config
|
||||
avatarProxy *avatar.Proxy
|
||||
conf *oauth2.Config
|
||||
}
|
||||
|
||||
// Params to make initialized and ready to use provider
|
||||
@@ -69,7 +69,7 @@ func initProvider(p Params, provider Provider) Provider {
|
||||
|
||||
provider.conf = &conf
|
||||
provider.Store = p.SessionStore
|
||||
provider.AvatarProxy = p.AvatarProxy
|
||||
provider.avatarProxy = p.AvatarProxy
|
||||
return provider
|
||||
}
|
||||
|
||||
@@ -166,8 +166,8 @@ func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("[DEBUG] got raw user info %+v", jData)
|
||||
|
||||
u := p.MapUser(jData, data)
|
||||
if p.AvatarProxy != nil {
|
||||
if avatarURL, e := p.AvatarProxy.Put(u); e == nil {
|
||||
if p.avatarProxy != nil {
|
||||
if avatarURL, e := p.avatarProxy.Put(u); e == nil {
|
||||
u.Picture = avatarURL
|
||||
} else {
|
||||
log.Printf("[WARN] failed to proxy avatar, %s", e)
|
||||
|
||||
@@ -29,6 +29,8 @@ type Proxy struct {
|
||||
RoutePath string
|
||||
}
|
||||
|
||||
const imgSfx = ".image"
|
||||
|
||||
// Put gets original avatar url from user info and returns proxied url
|
||||
func (p *Proxy) Put(u store.User) (avatarURL string, err error) {
|
||||
|
||||
@@ -39,6 +41,7 @@ func (p *Proxy) Put(u store.User) (avatarURL string, err error) {
|
||||
return "", errors.Errorf("no picture for %s", u.ID)
|
||||
}
|
||||
|
||||
// load avatar from remote location
|
||||
client := http.Client{Timeout: 10 * time.Second}
|
||||
resp, err := client.Get(u.Picture)
|
||||
if err != nil {
|
||||
@@ -49,13 +52,18 @@ func (p *Proxy) Put(u store.User) (avatarURL string, err error) {
|
||||
log.Printf("[WARN] can't close response body, %s", e)
|
||||
}
|
||||
}()
|
||||
|
||||
// get ID and location of locally cached avatar
|
||||
encID := p.encodeID(u.ID)
|
||||
location := p.location(encID)
|
||||
if err = os.Mkdir(location, 0700); err != nil && !strings.Contains(err.Error(), "file exists") {
|
||||
return "", errors.Wrapf(err, "failed to make avatar location %s", location)
|
||||
location := p.location(encID) // location adds partion to path
|
||||
|
||||
if _, err = os.Stat(location); os.IsNotExist(err) {
|
||||
if e := os.Mkdir(location, 0700); e != nil {
|
||||
return "", errors.Wrapf(e, "failed to mkdir avatar location %s", location)
|
||||
}
|
||||
}
|
||||
|
||||
avFile := path.Join(location, encID+".image")
|
||||
avFile := path.Join(location, encID+imgSfx)
|
||||
fh, err := os.Create(avFile)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "can't create file %s", avFile)
|
||||
@@ -71,15 +79,17 @@ func (p *Proxy) Put(u store.User) (avatarURL string, err error) {
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] saved avatar from %s to %s, user %q", u.Picture, avFile, u.Name)
|
||||
return p.RoutePath + "/" + encID + ".image", nil
|
||||
return p.RoutePath + "/" + encID + imgSfx, nil
|
||||
}
|
||||
|
||||
// Routes returns auth routes for given provider
|
||||
func (p *Proxy) Routes() chi.Router {
|
||||
router := chi.NewRouter()
|
||||
|
||||
// GET /123456789.image
|
||||
router.Get("/{avatar}", func(w http.ResponseWriter, r *http.Request) {
|
||||
avatar := chi.URLParam(r, "avatar")
|
||||
location := p.location(strings.TrimSuffix(avatar, ".image"))
|
||||
location := p.location(strings.TrimSuffix(avatar, imgSfx))
|
||||
avFile := path.Join(location, avatar)
|
||||
fh, err := os.Open(avFile)
|
||||
if err != nil {
|
||||
@@ -111,6 +121,7 @@ func (p *Proxy) Routes() chi.Router {
|
||||
return router
|
||||
}
|
||||
|
||||
// encodeID hashes user id to sha1
|
||||
func (p *Proxy) encodeID(id string) string {
|
||||
h := sha1.New()
|
||||
_, err := h.Write([]byte(id))
|
||||
@@ -121,7 +132,7 @@ func (p *Proxy) encodeID(id string) string {
|
||||
}
|
||||
|
||||
// get location for user id by adding partion to final path
|
||||
// the end result is a full path like this - /tmp/avatars.test/992
|
||||
// the end result is a full path like this - /tmp/avatars.test/92
|
||||
func (p *Proxy) location(id string) string {
|
||||
checksum64 := crc64.Checksum([]byte(id), crc64.MakeTable(crc64.ECMA))
|
||||
partition := checksum64 % 100
|
||||
|
||||
Reference in New Issue
Block a user