mirror of
https://github.com/elyby/chrly.git
synced 2025-05-31 14:11:51 +05:30
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7ac890812 | ||
|
|
b73582bbf4 | ||
|
|
34598e39bc | ||
|
|
7734f2cbd5 | ||
|
|
55b8c12955 | ||
|
|
10ff6f34fb | ||
|
|
31cd75ffa7 |
35
http/api.go
35
http/api.go
@@ -18,20 +18,11 @@ import (
|
||||
"github.com/thedevsaddam/govalidator"
|
||||
)
|
||||
|
||||
//noinspection GoSnakeCaseUsage
|
||||
const UUID_ANY = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
var regexUuidAny = regexp.MustCompile(UUID_ANY)
|
||||
|
||||
func init() {
|
||||
govalidator.AddCustomRule("md5", func(field string, rule string, message string, value interface{}) error {
|
||||
val := []byte(value.(string))
|
||||
if ok, _ := regexp.Match(`^[a-f0-9]{32}$`, val); !ok {
|
||||
if message == "" {
|
||||
message = fmt.Sprintf("The %s field must be a valid md5 hash", field)
|
||||
}
|
||||
|
||||
return errors.New(message)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
govalidator.AddCustomRule("skinUploadingNotAvailable", func(field string, rule string, message string, value interface{}) error {
|
||||
if message == "" {
|
||||
message = "Skin uploading is temporary unavailable"
|
||||
@@ -39,6 +30,20 @@ func init() {
|
||||
|
||||
return errors.New(message)
|
||||
})
|
||||
|
||||
// Add ability to validate any possible uuid form
|
||||
govalidator.AddCustomRule("uuid_any", func(field string, rule string, message string, value interface{}) error {
|
||||
str := value.(string)
|
||||
if !regexUuidAny.MatchString(str) {
|
||||
if message == "" {
|
||||
message = fmt.Sprintf("The %s field must contain valid UUID", field)
|
||||
}
|
||||
|
||||
return errors.New(message)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (cfg *Config) PostSkin(resp http.ResponseWriter, req *http.Request) {
|
||||
@@ -152,11 +157,11 @@ func validatePostSkinRequest(request *http.Request) map[string][]string {
|
||||
validationRules := govalidator.MapData{
|
||||
"identityId": {"required", "numeric", "min:1"},
|
||||
"username": {"required"},
|
||||
"uuid": {"required", "uuid"},
|
||||
"uuid": {"required", "uuid_any"},
|
||||
"skinId": {"required", "numeric", "min:1"},
|
||||
"url": {"url"},
|
||||
"file:skin": {"ext:png", "size:24576", "mime:image/png"},
|
||||
"hash": {"md5"},
|
||||
"hash": {},
|
||||
"is1_8": {"bool"},
|
||||
"isSlim": {"bool"},
|
||||
}
|
||||
|
||||
@@ -268,7 +268,6 @@ func TestConfig_PostSkin_RequiredFields(t *testing.T) {
|
||||
config, mocks := setupMocks(ctrl)
|
||||
|
||||
form := url.Values{
|
||||
"hash": {"this is not md5"},
|
||||
"mojangTextures": {"someBase64EncodedString"},
|
||||
}
|
||||
|
||||
@@ -307,9 +306,6 @@ func TestConfig_PostSkin_RequiredFields(t *testing.T) {
|
||||
"The uuid field is required",
|
||||
"The uuid field must contain valid UUID"
|
||||
],
|
||||
"hash": [
|
||||
"The hash field must be a valid md5 hash"
|
||||
],
|
||||
"url": [
|
||||
"One of url or skin should be provided, but not both"
|
||||
],
|
||||
|
||||
27
http/face.go
Normal file
27
http/face.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
const defaultHash = "default"
|
||||
|
||||
func (cfg *Config) Face(response http.ResponseWriter, request *http.Request) {
|
||||
cfg.Logger.IncCounter("faces.request", 1)
|
||||
username := parseUsername(mux.Vars(request)["username"])
|
||||
rec, err := cfg.SkinsRepo.FindByUsername(username)
|
||||
var hash string
|
||||
if err != nil || rec.SkinId == 0 {
|
||||
hash = defaultHash
|
||||
} else {
|
||||
hash = rec.Hash
|
||||
}
|
||||
|
||||
http.Redirect(response, request, buildFaceUrl(hash), 301)
|
||||
}
|
||||
|
||||
func buildFaceUrl(hash string) string {
|
||||
return "http://ely.by/minecraft/skin_buffer/faces/" + hash + ".png"
|
||||
}
|
||||
53
http/face_test.go
Normal file
53
http/face_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
testify "github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/elyby/chrly/db"
|
||||
)
|
||||
|
||||
func TestConfig_Face(t *testing.T) {
|
||||
assert := testify.New(t)
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
config, mocks := setupMocks(ctrl)
|
||||
|
||||
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
|
||||
mocks.Log.EXPECT().IncCounter("faces.request", int64(1))
|
||||
|
||||
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins/mock_user/face.png", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
config.CreateHandler().ServeHTTP(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
assert.Equal(301, resp.StatusCode)
|
||||
assert.Equal("http://ely.by/minecraft/skin_buffer/faces/55d2a8848764f5ff04012cdb093458bd.png", resp.Header.Get("Location"))
|
||||
}
|
||||
|
||||
func TestConfig_Face2(t *testing.T) {
|
||||
assert := testify.New(t)
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
config, mocks := setupMocks(ctrl)
|
||||
|
||||
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(nil, &db.SkinNotFoundError{"mock_user"})
|
||||
mocks.Log.EXPECT().IncCounter("faces.request", int64(1))
|
||||
|
||||
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins/mock_user/face.png", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
config.CreateHandler().ServeHTTP(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
assert.Equal(301, resp.StatusCode)
|
||||
assert.Equal("http://ely.by/minecraft/skin_buffer/faces/default.png", resp.Header.Get("Location"))
|
||||
}
|
||||
@@ -55,6 +55,8 @@ func (cfg *Config) CreateHandler() http.Handler {
|
||||
router.HandleFunc("/cloaks/{username}", cfg.Cape).Methods("GET").Name("cloaks")
|
||||
router.HandleFunc("/textures/{username}", cfg.Textures).Methods("GET")
|
||||
router.HandleFunc("/textures/signed/{username}", cfg.SignedTextures).Methods("GET")
|
||||
router.HandleFunc("/skins/{username}/face", cfg.Face).Methods("GET")
|
||||
router.HandleFunc("/skins/{username}/face.png", cfg.Face).Methods("GET")
|
||||
// Legacy
|
||||
router.HandleFunc("/skins", cfg.SkinGET).Methods("GET")
|
||||
router.HandleFunc("/cloaks", cfg.CapeGET).Methods("GET")
|
||||
|
||||
@@ -9,6 +9,7 @@ func (cfg *Config) NotFound(response http.ResponseWriter, request *http.Request)
|
||||
data, _ := json.Marshal(map[string]string{
|
||||
"status": "404",
|
||||
"message": "Not Found",
|
||||
"link": "http://docs.ely.by/skin-system.html",
|
||||
})
|
||||
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
|
||||
@@ -22,6 +22,7 @@ func TestConfig_NotFound(t *testing.T) {
|
||||
response, _ := ioutil.ReadAll(resp.Body)
|
||||
assert.JSONEq(`{
|
||||
"status": "404",
|
||||
"message": "Not Found"
|
||||
"message": "Not Found",
|
||||
"link": "http://docs.ely.by/skin-system.html"
|
||||
}`, string(response))
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ func (cfg *Config) SignedTextures(response http.ResponseWriter, request *http.Re
|
||||
Value: rec.MojangTextures,
|
||||
},
|
||||
{
|
||||
Name: "chrly",
|
||||
Value: "how do you tame a horse in Minecraft?",
|
||||
Name: "ely",
|
||||
Value: "but why are you asking?",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ func TestConfig_SignedTextures(t *testing.T) {
|
||||
"value": "mocked textures base64"
|
||||
},
|
||||
{
|
||||
"name": "chrly",
|
||||
"value": "how do you tame a horse in Minecraft?"
|
||||
"name": "ely",
|
||||
"value": "but why are you asking?"
|
||||
}
|
||||
]
|
||||
}`, string(response))
|
||||
|
||||
Reference in New Issue
Block a user