diff --git a/.gitignore b/.gitignore index 08c26dc..c153235 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ # IDEA /.idea + +# Docker Compose override file +docker-compose.override.yml diff --git a/Dockerfile b/Dockerfile index 2c08e63..c72d9d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,12 @@ FROM golang:1.7 -RUN mkdir -p /go/src/app +RUN mkdir -p /go/src/elyby/minecraft-skinsystem \ + && ln -s /go/src/elyby/minecraft-skinsystem /go/src/app + WORKDIR /go/src/app -COPY ./src /go/src/app +COPY ./minecraft-skinsystem.go /go/src/app/ +COPY ./lib /go/src/app/lib RUN go-wrapper download RUN go-wrapper install diff --git a/lib/routes/Skins.go b/lib/routes/Skins.go new file mode 100644 index 0000000..fd8eb6c --- /dev/null +++ b/lib/routes/Skins.go @@ -0,0 +1,21 @@ +package routes + +import ( + "net/http" + "github.com/gorilla/mux" + "log" + "elyby/minecraft-skinsystem/lib/tools" +) + +func GetSkin(w http.ResponseWriter, r *http.Request) { + username := tools.ParseUsername(mux.Vars(r)["username"]) + log.Println("request skin for username " + username); + rec, err := tools.FindRecord(username) + if (err != nil) { + http.Redirect(w, r, "http://skins.minecraft.net/MinecraftSkins/" + username + ".png", 301) + log.Println("Cannot get skin for username " + username) + return + } + + http.Redirect(w, r, rec.Url, 301); +} diff --git a/lib/routes/Textures.go b/lib/routes/Textures.go new file mode 100644 index 0000000..36fbf6c --- /dev/null +++ b/lib/routes/Textures.go @@ -0,0 +1,38 @@ +package routes + +import ( + "net/http" + "github.com/gorilla/mux" + "log" + "elyby/minecraft-skinsystem/lib/structures" + "encoding/json" + "elyby/minecraft-skinsystem/lib/tools" +) + +func GetTextures(w http.ResponseWriter, r *http.Request) { + username := tools.ParseUsername(mux.Vars(r)["username"]) + log.Println("request textures for username " + username) + + rec, err := tools.FindRecord(username) + if (err != nil || rec.SkinId == 0) { + rec.Url = "http://skins.minecraft.net/MinecraftSkins/" + username + ".png" + rec.Hash = string(tools.BuildNonElyTexturesHash(username)) + } + + textures := structures.TexturesResponse{ + Skin: &structures.Skin{ + Url: rec.Url, + Hash: rec.Hash, + }, + } + + if (rec.IsSlim) { + textures.Skin.Metadata = &structures.SkinMetadata{ + Model: "slim", + } + } + + response,_ := json.Marshal(textures) + w.Header().Set("Content-Type", "application/json") + w.Write(response) +} diff --git a/lib/services/services.go b/lib/services/services.go new file mode 100644 index 0000000..6f0421b --- /dev/null +++ b/lib/services/services.go @@ -0,0 +1,7 @@ +package services + +import ( + "github.com/mediocregopher/radix.v2/redis" +) + +var Redis *redis.Client diff --git a/lib/structures/Skin.go b/lib/structures/Skin.go new file mode 100644 index 0000000..93ee16e --- /dev/null +++ b/lib/structures/Skin.go @@ -0,0 +1,7 @@ +package structures + +type Skin struct { + Url string `json:"url"` + Hash string `json:"hash"` + Metadata *SkinMetadata `json:"metadata,omitempty"` +} diff --git a/lib/structures/SkinItem.go b/lib/structures/SkinItem.go new file mode 100644 index 0000000..8877975 --- /dev/null +++ b/lib/structures/SkinItem.go @@ -0,0 +1,11 @@ +package structures + +type SkinItem struct { + UserId int `json:"userId"` + Nickname string `json:"nickname"` + SkinId int `json:"skinId"` + Url string `json:"url"` + Is1_8 bool `json:"is1_8"` + IsSlim bool `json:"isSlim"` + Hash string `json:"hash"` +} diff --git a/lib/structures/SkinMetadata.go b/lib/structures/SkinMetadata.go new file mode 100644 index 0000000..31afd0d --- /dev/null +++ b/lib/structures/SkinMetadata.go @@ -0,0 +1,5 @@ +package structures + +type SkinMetadata struct { + Model string `json:"model"` +} diff --git a/lib/structures/TexturesResponse.go b/lib/structures/TexturesResponse.go new file mode 100644 index 0000000..687d53a --- /dev/null +++ b/lib/structures/TexturesResponse.go @@ -0,0 +1,5 @@ +package structures + +type TexturesResponse struct { + Skin *Skin `json:"SKIN"` +} diff --git a/lib/tools/tools.go b/lib/tools/tools.go new file mode 100644 index 0000000..2c0ff10 --- /dev/null +++ b/lib/tools/tools.go @@ -0,0 +1,48 @@ +package tools + +import ( + "strings" + "time" + "crypto/md5" + "strconv" + "encoding/hex" + "elyby/minecraft-skinsystem/lib/structures" + "elyby/minecraft-skinsystem/lib/services" + "encoding/json" + "log" +) + +func ParseUsername(username string) string { + const suffix = ".png" + if strings.HasSuffix(username, suffix) { + username = strings.TrimSuffix(username, suffix) + } + + return username +} + +func BuildNonElyTexturesHash(username string) string { + n := time.Now() + hour := time.Date(n.Year(), n.Month(), n.Day(), n.Hour(), 0, 0, 0, time.UTC).Unix() + hasher := md5.New() + hasher.Write([]byte("non-ely-" + strconv.FormatInt(hour, 10) + "-" + username)) + + return hex.EncodeToString(hasher.Sum(nil)) +} + +func FindRecord(username string) (structures.SkinItem, error) { + var record structures.SkinItem; + result, err := services.Redis.Cmd("GET", BuildKey(username)).Str(); + if (err == nil) { + decodeErr := json.Unmarshal([]byte(result), &record) + if (decodeErr != nil) { + log.Println("Cannot decode record data") + } + } + + return record, err +} + +func BuildKey(username string) string { + return "username:" + strings.ToLower(username) +} diff --git a/minecraft-skinsystem.go b/minecraft-skinsystem.go new file mode 100644 index 0000000..662fe5d --- /dev/null +++ b/minecraft-skinsystem.go @@ -0,0 +1,31 @@ +package main + +import ( + "log" + "net/http" + + "github.com/gorilla/mux" + "github.com/mediocregopher/radix.v2/redis" + + "elyby/minecraft-skinsystem/lib/routes" + "elyby/minecraft-skinsystem/lib/services" +) + +var client, redisErr = redis.Dial("tcp", "redis:6379") + +func main() { + if redisErr != nil { + log.Fatal("Redis unavailable") + } + + services.Redis = client + + router := mux.NewRouter().StrictSlash(true) + router.HandleFunc("/skins/{username}", routes.GetSkin) + router.HandleFunc("/textures/{username}", routes.GetTextures) + router.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Hello")) + }) + + log.Fatal(http.ListenAndServe(":80", router)) +} diff --git a/src/main.go b/src/main.go deleted file mode 100644 index 1e1406b..0000000 --- a/src/main.go +++ /dev/null @@ -1,133 +0,0 @@ -package main - -import ( - "log" - "net/http" - - "github.com/gorilla/mux" - "github.com/mediocregopher/radix.v2/redis" - "encoding/json" - "strings" - "time" - "strconv" - "crypto/md5" - "encoding/hex" -) - -var client, redisErr = redis.Dial("tcp", "redis:6379") - -func main() { - if redisErr != nil { - log.Fatal("Redis unavailable") - } - - router := mux.NewRouter().StrictSlash(true) - router.HandleFunc("/skins/{username}", GetSkin) - router.HandleFunc("/textures/{username}", GetTextures) - - log.Fatal(http.ListenAndServe(":80", router)) -} - -func GetSkin(w http.ResponseWriter, r *http.Request) { - username := ParseUsername(mux.Vars(r)["username"]) - log.Println("request skin for username " + username); - rec, err := FindRecord(username) - if (err != nil) { - http.Redirect(w, r, "http://skins.minecraft.net/MinecraftSkins/" + username + ".png", 301) - log.Println("Cannot get skin for username " + username) - return - } - - http.Redirect(w, r, rec.Url, 301); -} - -func GetTextures(w http.ResponseWriter, r *http.Request) { - username := ParseUsername(mux.Vars(r)["username"]) - log.Println("request textures for username " + username) - - rec, err := FindRecord(username) - if (err != nil || rec.SkinId == 0) { - rec.Url = "http://skins.minecraft.net/MinecraftSkins/" + username + ".png" - rec.Hash = string(BuildNonElyTexturesHash(username)) - } - - textures := TexturesResponse{ - Skin: &Skin{ - Url: rec.Url, - Hash: rec.Hash, - }, - } - - if (rec.IsSlim) { - textures.Skin.Metadata = &SkinMetadata{ - Model: "slim", - } - } - - response,_ := json.Marshal(textures) - w.Header().Set("Content-Type", "application/json") - w.Write(response) -} - -// STRUCTURES - -type SkinItem struct { - UserId int `json:"userId"` - Nickname string `json:"nickname"` - SkinId int `json:"skinId"` - Url string `json:"url"` - Is1_8 bool `json:"is1_8"` - IsSlim bool `json:"isSlim"` - Hash string `json:"hash"` -} - -type TexturesResponse struct { - Skin *Skin `json:"SKIN"` -} - -type Skin struct { - Url string `json:"url"` - Hash string `json:"hash"` - Metadata *SkinMetadata `json:"metadata,omitempty"` -} - -type SkinMetadata struct { - Model string `json:"model"` -} - -// TOOLS - -func ParseUsername(username string) string { - const suffix = ".png" - if strings.HasSuffix(username, suffix) { - username = strings.TrimSuffix(username, suffix) - } - - return username -} - -func BuildNonElyTexturesHash(username string) string { - n := time.Now() - hour := time.Date(n.Year(), n.Month(), n.Day(), n.Hour(), 0, 0, 0, time.UTC).Unix() - hasher := md5.New() - hasher.Write([]byte("non-ely-" + strconv.FormatInt(hour, 10) + "-" + username)) - - return hex.EncodeToString(hasher.Sum(nil)) -} - -func FindRecord(username string) (SkinItem, error) { - var record SkinItem; - result, err := client.Cmd("GET", BuildKey(username)).Str(); - if (err == nil) { - decodeErr := json.Unmarshal([]byte(result), &record) - if (decodeErr != nil) { - log.Println("Cannot decode record data") - } - } - - return record, err -} - -func BuildKey(username string) string { - return "username:" + strings.ToLower(username) -}