mirror of
https://github.com/elyby/chrly.git
synced 2025-05-31 14:11:51 +05:30
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a661f9aac3 | ||
|
|
9ffdf99b77 | ||
|
|
ad35872fc1 | ||
|
|
a8d8fffaa5 | ||
|
|
0d41f0c347 | ||
|
|
b22f0551fa | ||
|
|
1a906cfc09 | ||
|
|
f610667aa5 | ||
|
|
8b51c1bd0c | ||
|
|
cbe940f8ec | ||
|
|
8693673a71 | ||
|
|
73205648d2 | ||
|
|
3d73cc9402 | ||
|
|
39f5ec5bee | ||
|
|
e652691b29 | ||
|
|
d3b4bee3b0 | ||
|
|
ae50e90ea7 | ||
|
|
c74151c558 | ||
|
|
445bd18fbc | ||
|
|
6a881a62e3 | ||
|
|
201a257d69 | ||
|
|
5d46094643 | ||
|
|
1694403c79 | ||
|
|
66c61dc3cd | ||
|
|
a0d940f8cd |
33
.gitlab-ci.yml
Normal file
33
.gitlab-ci.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
image: docker:latest
|
||||
|
||||
stages:
|
||||
- build
|
||||
- push
|
||||
|
||||
before_script:
|
||||
- docker login -u gitlab-ci -p $CI_BUILD_TOKEN registry.ely.by
|
||||
|
||||
variables:
|
||||
CONTAINER_IMAGE: registry.ely.by/elyby/skinsystem
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script:
|
||||
- export IMAGE_NAME="$CONTAINER_IMAGE:dev"
|
||||
- docker build --pull -t $IMAGE_NAME .
|
||||
- docker push $IMAGE_NAME
|
||||
only:
|
||||
- develop
|
||||
|
||||
push_tags:
|
||||
stage: push
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
- export IMAGE_NAME="$CONTAINER_IMAGE:$CI_BUILD_TAG"
|
||||
- docker tag $CONTAINER_IMAGE:dev $CONTAINER_IMAGE:latest
|
||||
- docker tag $CONTAINER_IMAGE:latest $IMAGE_NAME
|
||||
- docker push $IMAGE_NAME
|
||||
- docker push $CONTAINER_IMAGE:latest
|
||||
only:
|
||||
- tags
|
||||
2
data/statsd/.gitignore
vendored
Normal file
2
data/statsd/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,7 +1,7 @@
|
||||
version: '2'
|
||||
services:
|
||||
redis:
|
||||
image: redis:3.0-alpine
|
||||
image: redis:3.2-32bit
|
||||
volumes:
|
||||
- ./data/redis:/data
|
||||
|
||||
@@ -11,3 +11,10 @@ services:
|
||||
RABBITMQ_DEFAULT_USER: "ely-skinsystem-app"
|
||||
RABBITMQ_DEFAULT_PASS: "ely-skinsystem-app-password"
|
||||
RABBITMQ_DEFAULT_VHOST: "/ely"
|
||||
|
||||
statsd:
|
||||
image: hopsoft/graphite-statsd
|
||||
volumes:
|
||||
- ./data/statsd:/opt/graphite/storage
|
||||
- ./data/graphite-config:/opt/graphite/conf
|
||||
- ./data/statsd-config/config.json:/opt/statsd/config.js
|
||||
|
||||
@@ -11,6 +11,11 @@ services:
|
||||
links:
|
||||
- redis
|
||||
- rabbitmq
|
||||
- statsd
|
||||
environment:
|
||||
ACCOUNTS_API_ID: ""
|
||||
ACCOUNTS_API_SECRET: ""
|
||||
STATSD_ADDR: ""
|
||||
|
||||
redis:
|
||||
extends:
|
||||
@@ -21,3 +26,10 @@ services:
|
||||
extends:
|
||||
file: docker-compose.base.yml
|
||||
service: rabbitmq
|
||||
|
||||
statsd:
|
||||
extends:
|
||||
file: docker-compose.base.yml
|
||||
service: statsd
|
||||
ports:
|
||||
- "8123:80"
|
||||
|
||||
@@ -8,6 +8,10 @@ services:
|
||||
- redis
|
||||
- rabbitmq
|
||||
restart: always
|
||||
environment:
|
||||
ACCOUNTS_API_ID: ""
|
||||
ACCOUNTS_API_SECRET: ""
|
||||
STATSD_ADDR: ""
|
||||
|
||||
redis:
|
||||
extends:
|
||||
|
||||
14
lib/data/SignedTexturesResponse.go
Normal file
14
lib/data/SignedTexturesResponse.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package data
|
||||
|
||||
type SignedTexturesResponse struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IsEly bool `json:"ely,omitempty"`
|
||||
Props []Property `json:"properties"`
|
||||
}
|
||||
|
||||
type Property struct {
|
||||
Name string `json:"name"`
|
||||
Signature string `json:"signature,omitempty"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
@@ -12,14 +12,17 @@ import (
|
||||
)
|
||||
|
||||
type SkinItem struct {
|
||||
UserId int `json:"userId"`
|
||||
Username string `json:"username"`
|
||||
SkinId int `json:"skinId"`
|
||||
Url string `json:"url"`
|
||||
Is1_8 bool `json:"is1_8"`
|
||||
IsSlim bool `json:"isSlim"`
|
||||
Hash string `json:"hash"`
|
||||
oldUsername string
|
||||
UserId int `json:"userId"`
|
||||
Uuid string `json:"uuid"`
|
||||
Username string `json:"username"`
|
||||
SkinId int `json:"skinId"`
|
||||
Url string `json:"url"`
|
||||
Is1_8 bool `json:"is1_8"`
|
||||
IsSlim bool `json:"isSlim"`
|
||||
Hash string `json:"hash"`
|
||||
MojangTextures string `json:"mojangTextures"`
|
||||
MojangSignature string `json:"mojangSignature"`
|
||||
oldUsername string
|
||||
}
|
||||
|
||||
const accountIdToUsernameKey string = "hash:username-to-account-id"
|
||||
@@ -62,13 +65,16 @@ func (s *SkinItem) Delete() {
|
||||
|
||||
func FindSkinByUsername(username string) (SkinItem, error) {
|
||||
var record SkinItem;
|
||||
services.Logger.IncCounter("storage.query", 1)
|
||||
response := services.RedisPool.Cmd("GET", tools.BuildKey(username));
|
||||
if (response.IsType(redis.Nil)) {
|
||||
services.Logger.IncCounter("storage.not_found", 1)
|
||||
return record, SkinNotFound{username}
|
||||
}
|
||||
|
||||
result, err := response.Str()
|
||||
if (err == nil) {
|
||||
services.Logger.IncCounter("storage.found", 1)
|
||||
decodeErr := json.Unmarshal([]byte(result), &record)
|
||||
if (decodeErr != nil) {
|
||||
log.Println("Cannot decode record data")
|
||||
|
||||
44
lib/external/accounts/AccountInfo.go
vendored
Normal file
44
lib/external/accounts/AccountInfo.go
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type AccountInfoResponse struct {
|
||||
Id int `json:"id"`
|
||||
Uuid string `json:"uuid"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
const internalAccountInfoUrl = domain + "/api/internal/accounts/info"
|
||||
|
||||
func (token *Token) AccountInfo(attribute string, value string) (AccountInfoResponse, error) {
|
||||
request, err := http.NewRequest("GET", internalAccountInfoUrl, nil)
|
||||
request.Header.Add("Authorization", "Bearer " + token.AccessToken)
|
||||
query := request.URL.Query()
|
||||
query.Add(attribute, value)
|
||||
request.URL.RawQuery = query.Encode()
|
||||
|
||||
response, err := Client.Do(request)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
var info AccountInfoResponse
|
||||
|
||||
responseError := handleResponse(response)
|
||||
if responseError != nil {
|
||||
return info, responseError
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(response.Body)
|
||||
println("Raw account info response is " + string(body))
|
||||
json.Unmarshal(body, &info)
|
||||
|
||||
return info, nil
|
||||
}
|
||||
49
lib/external/accounts/GetToken.go
vendored
Normal file
49
lib/external/accounts/GetToken.go
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"net/url"
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type TokenRequest struct {
|
||||
Id string
|
||||
Secret string
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}
|
||||
|
||||
const tokenUrl = domain + "/api/oauth2/v1/token"
|
||||
|
||||
func GetToken(request TokenRequest) (Token, error) {
|
||||
form := url.Values{}
|
||||
form.Add("client_id", request.Id)
|
||||
form.Add("client_secret", request.Secret)
|
||||
form.Add("grant_type", "client_credentials")
|
||||
form.Add("scope", strings.Join(request.Scopes, ","))
|
||||
|
||||
response, err := Client.Post(tokenUrl, "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
var result Token
|
||||
responseError := handleResponse(response)
|
||||
if responseError != nil {
|
||||
return result, responseError
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(response.Body)
|
||||
|
||||
json.Unmarshal(body, &result)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
51
lib/external/accounts/base.go
vendored
Normal file
51
lib/external/accounts/base.go
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const domain = "https://account.ely.by"
|
||||
|
||||
var Client = &http.Client{}
|
||||
|
||||
type UnauthorizedResponse struct {}
|
||||
|
||||
func (err UnauthorizedResponse) Error() string {
|
||||
return "Unauthorized response"
|
||||
}
|
||||
|
||||
type ForbiddenResponse struct {}
|
||||
|
||||
func (err ForbiddenResponse) Error() string {
|
||||
return "Forbidden response"
|
||||
}
|
||||
|
||||
type NotFoundResponse struct {}
|
||||
|
||||
func (err NotFoundResponse) Error() string {
|
||||
return "Not found"
|
||||
}
|
||||
|
||||
type NotSuccessResponse struct {
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func (err NotSuccessResponse) Error() string {
|
||||
return fmt.Sprintf("Response code is \"%d\"", err.StatusCode)
|
||||
}
|
||||
|
||||
func handleResponse(response *http.Response) error {
|
||||
switch status := response.StatusCode; status {
|
||||
case 200:
|
||||
return nil
|
||||
case 401:
|
||||
return &UnauthorizedResponse{}
|
||||
case 403:
|
||||
return &ForbiddenResponse{}
|
||||
case 404:
|
||||
return &NotFoundResponse{}
|
||||
default:
|
||||
return &NotSuccessResponse{status}
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,21 @@ package routes
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"elyby/minecraft-skinsystem/lib/tools"
|
||||
"elyby/minecraft-skinsystem/lib/data"
|
||||
"elyby/minecraft-skinsystem/lib/services"
|
||||
)
|
||||
|
||||
func Cape(response http.ResponseWriter, request *http.Request) {
|
||||
if (mux.Vars(request)["converted"] == "") {
|
||||
services.Logger.IncCounter("capes.request", 1)
|
||||
}
|
||||
|
||||
username := tools.ParseUsername(mux.Vars(request)["username"])
|
||||
log.Println("request cape for username " + username)
|
||||
rec, err := data.FindCapeByUsername(username)
|
||||
if (err != nil) {
|
||||
http.Redirect(response, request, "http://skins.minecraft.net/MinecraftCloaks/" + username + ".png", 301)
|
||||
@@ -24,6 +27,7 @@ func Cape(response http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
|
||||
func CapeGET(w http.ResponseWriter, r *http.Request) {
|
||||
services.Logger.IncCounter("capes.get_request", 1)
|
||||
username := r.URL.Query().Get("name")
|
||||
if username == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
@@ -31,5 +35,6 @@ func CapeGET(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
mux.Vars(r)["username"] = username
|
||||
mux.Vars(r)["converted"] = "1"
|
||||
Cape(w, r)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
@@ -14,7 +13,6 @@ const defaultHash = "default"
|
||||
|
||||
func Face(w http.ResponseWriter, r *http.Request) {
|
||||
username := tools.ParseUsername(mux.Vars(r)["username"])
|
||||
log.Println("request skin for username " + username);
|
||||
rec, err := data.FindSkinByUsername(username)
|
||||
var hash string
|
||||
if (err != nil || rec.SkinId == 0) {
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"elyby/minecraft-skinsystem/lib/services"
|
||||
)
|
||||
|
||||
// Метод-наследие от первой версии системы скинов.
|
||||
@@ -18,11 +20,15 @@ func MinecraftPHP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
mux.Vars(r)["username"] = username
|
||||
mux.Vars(r)["converted"] = "1"
|
||||
switch required {
|
||||
case "skin": Skin(w, r)
|
||||
case "cloack": Cape(w, r)
|
||||
default: {
|
||||
case "skin":
|
||||
services.Logger.IncCounter("skins.minecraft-php-request", 1)
|
||||
Skin(w, r)
|
||||
case "cloack":
|
||||
services.Logger.IncCounter("capes.minecraft-php-request", 1)
|
||||
Cape(w, r)
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"elyby/minecraft-skinsystem/lib/data"
|
||||
)
|
||||
|
||||
func SetSkin(w http.ResponseWriter, r *http.Request) {
|
||||
key := r.Header.Get("X-Ely-key")
|
||||
if key != "43fd2ce61b3f5704dfd729c1f2d6ffdb" {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
w.Write([]byte("Nice try"))
|
||||
return
|
||||
}
|
||||
|
||||
skin := new(data.SkinItem)
|
||||
skin.Username = r.PostFormValue("username")
|
||||
skin.UserId, _ = strconv.Atoi(r.PostFormValue("userId"))
|
||||
skin.SkinId, _ = strconv.Atoi(r.PostFormValue("skinId"))
|
||||
skin.Hash = r.PostFormValue("hash")
|
||||
skin.Is1_8, _ = strconv.ParseBool(r.PostFormValue("is1_8"))
|
||||
skin.IsSlim, _ = strconv.ParseBool(r.PostFormValue("isSlim"))
|
||||
skin.Url = r.PostFormValue("url")
|
||||
skin.Save()
|
||||
|
||||
w.Write([]byte("OK"))
|
||||
}
|
||||
44
lib/routes/SignedTextures.go
Normal file
44
lib/routes/SignedTextures.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"elyby/minecraft-skinsystem/lib/data"
|
||||
"elyby/minecraft-skinsystem/lib/tools"
|
||||
"elyby/minecraft-skinsystem/lib/services"
|
||||
)
|
||||
|
||||
func SignedTextures(w http.ResponseWriter, r *http.Request) {
|
||||
services.Logger.IncCounter("signed_textures.request", 1)
|
||||
username := tools.ParseUsername(mux.Vars(r)["username"])
|
||||
|
||||
rec, err := data.FindSkinByUsername(username)
|
||||
if (err != nil || rec.SkinId == 0 || rec.MojangTextures == "") {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
responseData:= data.SignedTexturesResponse{
|
||||
Id: strings.Replace(rec.Uuid, "-", "", -1),
|
||||
Name: rec.Username,
|
||||
Props: []data.Property{
|
||||
{
|
||||
Name: "textures",
|
||||
Signature: rec.MojangSignature,
|
||||
Value: rec.MojangTextures,
|
||||
},
|
||||
{
|
||||
Name: "ely",
|
||||
Value: "but why are you asking?",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
response,_ := json.Marshal(responseData)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(response)
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"elyby/minecraft-skinsystem/lib/tools"
|
||||
"elyby/minecraft-skinsystem/lib/data"
|
||||
"elyby/minecraft-skinsystem/lib/services"
|
||||
)
|
||||
|
||||
func Skin(w http.ResponseWriter, r *http.Request) {
|
||||
if (mux.Vars(r)["converted"] == "") {
|
||||
services.Logger.IncCounter("skins.request", 1)
|
||||
}
|
||||
|
||||
username := tools.ParseUsername(mux.Vars(r)["username"])
|
||||
log.Println("request skin for username " + username);
|
||||
rec, err := data.FindSkinByUsername(username)
|
||||
if (err != nil) {
|
||||
http.Redirect(w, r, "http://skins.minecraft.net/MinecraftSkins/" + username + ".png", 301)
|
||||
@@ -23,6 +26,7 @@ func Skin(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func SkinGET(w http.ResponseWriter, r *http.Request) {
|
||||
services.Logger.IncCounter("skins.get_request", 1)
|
||||
username := r.URL.Query().Get("name")
|
||||
if username == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
@@ -30,5 +34,6 @@ func SkinGET(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
mux.Vars(r)["username"] = username
|
||||
mux.Vars(r)["converted"] = "1"
|
||||
Skin(w, r)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
)
|
||||
|
||||
func Textures(w http.ResponseWriter, r *http.Request) {
|
||||
services.Logger.IncCounter("textures.request", 1)
|
||||
username := tools.ParseUsername(mux.Vars(r)["username"])
|
||||
log.Println("request textures for username " + username)
|
||||
|
||||
rec, err := data.FindSkinByUsername(username)
|
||||
if (err != nil || rec.SkinId == 0) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/mediocregopher/radix.v2/pool"
|
||||
"github.com/streadway/amqp"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mono83/slf/wd"
|
||||
)
|
||||
|
||||
var Router *mux.Router
|
||||
@@ -13,3 +14,5 @@ var RedisPool *pool.Pool
|
||||
var RabbitMQChannel *amqp.Channel
|
||||
|
||||
var RootFolder string
|
||||
|
||||
var Logger wd.Watchdog
|
||||
|
||||
@@ -30,7 +30,12 @@ func BuildKey(username string) string {
|
||||
}
|
||||
|
||||
func BuildElyUrl(route string) string {
|
||||
return "http://ely.by" + route
|
||||
prefix := "http://ely.by"
|
||||
if !strings.HasPrefix(route, prefix) {
|
||||
route = prefix + route
|
||||
}
|
||||
|
||||
return route
|
||||
}
|
||||
|
||||
func getCurrentHour() int64 {
|
||||
|
||||
@@ -20,3 +20,13 @@ func TestBuildKey(t *testing.T) {
|
||||
t.Error("Function shound convert string to lower case and concatenate it with usernmae:")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildElyUrl(t *testing.T) {
|
||||
if BuildElyUrl("/route") != "http://ely.by/route" {
|
||||
t.Error("Function should add prefix to the provided relative url.")
|
||||
}
|
||||
|
||||
if BuildElyUrl("http://ely.by/test/route") != "http://ely.by/test/route" {
|
||||
t.Error("Function should do not add prefix to the provided prefixed url.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"elyby/minecraft-skinsystem/lib/data"
|
||||
"log"
|
||||
"elyby/minecraft-skinsystem/lib/services"
|
||||
)
|
||||
|
||||
func handleChangeUsername(model usernameChanged) (bool) {
|
||||
if (model.OldUsername == "") {
|
||||
services.Logger.IncCounter("worker.change_username.empty_old_username", 1)
|
||||
record := data.SkinItem{
|
||||
UserId: model.AccountId,
|
||||
Username: model.NewUsername,
|
||||
@@ -17,36 +19,64 @@ func handleChangeUsername(model usernameChanged) (bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
record, err := data.FindSkinByUsername(model.OldUsername)
|
||||
record, err := data.FindSkinById(model.AccountId)
|
||||
if (err != nil) {
|
||||
log.Println("Exit by not found record")
|
||||
// TODO: я не уверен, что это валидное поведение
|
||||
// Суть в том, что здесь может возникнуть ошибка в том случае, если записи в базе нету
|
||||
// а значит его нужно, как минимум, зарегистрировать
|
||||
return true
|
||||
services.Logger.IncCounter("worker.change_username.id_not_found", 1)
|
||||
fmt.Println("Cannot find user id. Trying to search.")
|
||||
response, err := getById(model.AccountId)
|
||||
if err != nil {
|
||||
services.Logger.IncCounter("worker.change_username.id_not_restored", 1)
|
||||
fmt.Printf("Cannot restore user info. %T\n", err)
|
||||
// TODO: логгировать в какой-нибудь Sentry, если там не 404
|
||||
return true
|
||||
}
|
||||
|
||||
services.Logger.IncCounter("worker.change_username.id_restored", 1)
|
||||
fmt.Println("User info successfully restored.")
|
||||
record = data.SkinItem{
|
||||
UserId: response.Id,
|
||||
}
|
||||
}
|
||||
|
||||
record.Username = model.NewUsername
|
||||
record.Save()
|
||||
|
||||
log.Println("all saved!")
|
||||
services.Logger.IncCounter("worker.change_username.processed", 1)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func handleSkinChanged(model skinChanged) (bool) {
|
||||
func handleSkinChanged(model skinChanged) bool {
|
||||
record, err := data.FindSkinById(model.AccountId)
|
||||
if (err != nil) {
|
||||
return true
|
||||
if err != nil {
|
||||
services.Logger.IncCounter("worker.skin_changed.id_not_found", 1)
|
||||
fmt.Println("Cannot find user id. Trying to search.")
|
||||
response, err := getById(model.AccountId)
|
||||
if err != nil {
|
||||
services.Logger.IncCounter("worker.skin_changed.id_not_restored", 1)
|
||||
fmt.Printf("Cannot restore user info. %T\n", err)
|
||||
// TODO: логгировать в какой-нибудь Sentry, если там не 404
|
||||
return true
|
||||
}
|
||||
|
||||
services.Logger.IncCounter("worker.skin_changed.id_restored", 1)
|
||||
fmt.Println("User info successfully restored.")
|
||||
record.UserId = response.Id
|
||||
record.Username = response.Username
|
||||
}
|
||||
|
||||
record.Uuid = model.Uuid
|
||||
record.SkinId = model.SkinId
|
||||
record.Hash = model.Hash
|
||||
record.Is1_8 = model.Is1_8
|
||||
record.Hash = model.Hash
|
||||
record.Is1_8 = model.Is1_8
|
||||
record.IsSlim = model.IsSlim
|
||||
record.Url = model.Url
|
||||
record.Url = model.Url
|
||||
record.MojangTextures = model.MojangTextures
|
||||
record.MojangSignature = model.MojangSignature
|
||||
|
||||
record.Save()
|
||||
|
||||
services.Logger.IncCounter("worker.skin_changed.processed", 1)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -7,11 +7,14 @@ type usernameChanged struct {
|
||||
}
|
||||
|
||||
type skinChanged struct {
|
||||
AccountId int `json:"userId"`
|
||||
SkinId int `json:"skinId"`
|
||||
OldSkinId int `json:"oldSkinId"`
|
||||
Hash string `json:"hash"`
|
||||
Is1_8 bool `json:"is1_8"`
|
||||
IsSlim bool `json:"isSlim"`
|
||||
Url string `json:"url"`
|
||||
AccountId int `json:"userId"`
|
||||
Uuid string `json:"uuid"`
|
||||
SkinId int `json:"skinId"`
|
||||
OldSkinId int `json:"oldSkinId"`
|
||||
Hash string `json:"hash"`
|
||||
Is1_8 bool `json:"is1_8"`
|
||||
IsSlim bool `json:"isSlim"`
|
||||
Url string `json:"url"`
|
||||
MojangTextures string `json:"mojangTextures"`
|
||||
MojangSignature string `json:"mojangSignature"`
|
||||
}
|
||||
|
||||
60
lib/worker/supports.go
Normal file
60
lib/worker/supports.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"elyby/minecraft-skinsystem/lib/external/accounts"
|
||||
)
|
||||
|
||||
var AccountsTokenConfig *accounts.TokenRequest
|
||||
|
||||
var token *accounts.Token
|
||||
|
||||
const repeatsLimit = 3
|
||||
var repeatsCount = 0
|
||||
|
||||
func getById(id int) (accounts.AccountInfoResponse, error) {
|
||||
return _getByField("id", strconv.Itoa(id))
|
||||
}
|
||||
|
||||
func _getByField(field string, value string) (accounts.AccountInfoResponse, error) {
|
||||
defer resetRepeatsCount()
|
||||
|
||||
apiToken, err := getToken()
|
||||
if err != nil {
|
||||
return accounts.AccountInfoResponse{}, err
|
||||
}
|
||||
|
||||
result, err := apiToken.AccountInfo(field, value)
|
||||
if err != nil {
|
||||
_, ok := err.(*accounts.UnauthorizedResponse)
|
||||
if !ok || repeatsCount >= repeatsLimit {
|
||||
return accounts.AccountInfoResponse{}, err
|
||||
}
|
||||
|
||||
repeatsCount++
|
||||
token = nil
|
||||
|
||||
return _getByField(field, value)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getToken() (*accounts.Token, error) {
|
||||
if token == nil {
|
||||
println("token is nil, trying to obtain new one")
|
||||
tempToken, err := accounts.GetToken(*AccountsTokenConfig)
|
||||
if err != nil {
|
||||
println("cannot obtain new one token", err)
|
||||
return &accounts.Token{}, err
|
||||
}
|
||||
|
||||
token = &tempToken
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func resetRepeatsCount() {
|
||||
repeatsCount = 0
|
||||
}
|
||||
@@ -11,10 +11,15 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/streadway/amqp"
|
||||
"github.com/mediocregopher/radix.v2/pool"
|
||||
"github.com/mono83/slf/wd"
|
||||
"github.com/mono83/slf/rays"
|
||||
"github.com/mono83/slf/recievers/ansi"
|
||||
"github.com/mono83/slf/recievers/statsd"
|
||||
|
||||
"elyby/minecraft-skinsystem/lib/routes"
|
||||
"elyby/minecraft-skinsystem/lib/services"
|
||||
"elyby/minecraft-skinsystem/lib/worker"
|
||||
"elyby/minecraft-skinsystem/lib/external/accounts"
|
||||
)
|
||||
|
||||
const redisPoolSize int = 10
|
||||
@@ -24,6 +29,20 @@ func main() {
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
accountsApiId := os.Getenv("ACCOUNTS_API_ID")
|
||||
accountsApiSecret := os.Getenv("ACCOUNTS_API_SECRET")
|
||||
if accountsApiId == "" || accountsApiSecret == "" {
|
||||
log.Fatal("ACCOUNTS_API params must be provided")
|
||||
}
|
||||
|
||||
worker.AccountsTokenConfig = &accounts.TokenRequest{
|
||||
Id: accountsApiId,
|
||||
Secret: accountsApiSecret,
|
||||
Scopes: []string{
|
||||
"internal_account_info",
|
||||
},
|
||||
}
|
||||
|
||||
log.Println("Connecting to redis")
|
||||
|
||||
var redisString = os.Getenv("REDIS_ADDR")
|
||||
@@ -56,10 +75,32 @@ func main() {
|
||||
}
|
||||
log.Println("Connected to rabbitmq channel")
|
||||
|
||||
// statsd
|
||||
var statsdString = os.Getenv("STATSD_ADDR")
|
||||
if (statsdString != "") {
|
||||
log.Println("Connecting to statsd")
|
||||
hostname, _ := os.Hostname()
|
||||
statsdReceiver, err := statsd.NewReceiver(statsd.Config{
|
||||
Address: statsdString,
|
||||
Prefix: "ely.skinsystem." + hostname + ".app.",
|
||||
FlushEvery: 1,
|
||||
})
|
||||
if (err != nil) {
|
||||
log.Fatal("statsd connection error")
|
||||
}
|
||||
|
||||
wd.AddReceiver(statsdReceiver)
|
||||
} else {
|
||||
wd.AddReceiver(ansi.New(true, true, false))
|
||||
}
|
||||
|
||||
logger := wd.New("", "").WithParams(rays.Host)
|
||||
|
||||
router := mux.NewRouter().StrictSlash(true)
|
||||
router.HandleFunc("/skins/{username}", routes.Skin).Methods("GET").Name("skins")
|
||||
router.HandleFunc("/cloaks/{username}", routes.Cape).Methods("GET").Name("cloaks")
|
||||
router.HandleFunc("/textures/{username}", routes.Textures).Methods("GET").Name("textures")
|
||||
router.HandleFunc("/textures/signed/{username}", routes.SignedTextures).Methods("GET").Name("signedTextures")
|
||||
router.HandleFunc("/skins/{username}/face", routes.Face).Methods("GET").Name("faces")
|
||||
router.HandleFunc("/skins/{username}/face.png", routes.Face).Methods("GET").Name("faces")
|
||||
// Legacy
|
||||
@@ -69,12 +110,10 @@ func main() {
|
||||
// 404
|
||||
router.NotFoundHandler = http.HandlerFunc(routes.NotFound)
|
||||
|
||||
apiRouter := router.PathPrefix("/api").Subrouter()
|
||||
apiRouter.HandleFunc("/user/{username}/skin", routes.SetSkin).Methods("POST")
|
||||
|
||||
services.Router = router
|
||||
services.RedisPool = redisPool
|
||||
services.RabbitMQChannel = rabbitChannel
|
||||
services.Logger = logger
|
||||
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
services.RootFolder = filepath.Dir(file)
|
||||
|
||||
Reference in New Issue
Block a user