Merge branch 'develop'

This commit is contained in:
ErickSkrauch 2017-04-10 20:38:35 +03:00
commit 8b51c1bd0c
8 changed files with 258 additions and 9 deletions

View File

@ -12,6 +12,10 @@ services:
- redis - redis
- rabbitmq - rabbitmq
- statsd - statsd
environment:
ACCOUNTS_API_ID: ""
ACCOUNTS_API_SECRET: ""
STATSD_ADDR: ""
redis: redis:
extends: extends:

View File

@ -8,6 +8,10 @@ services:
- redis - redis
- rabbitmq - rabbitmq
restart: always restart: always
environment:
ACCOUNTS_API_ID: ""
ACCOUNTS_API_SECRET: ""
STATSD_ADDR: ""
redis: redis:
extends: extends:

44
lib/external/accounts/AccountInfo.go vendored Normal file
View 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
View 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
View File

@ -0,0 +1,51 @@
package accounts
import (
"fmt"
"net/http"
)
const domain = "https://dev.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}
}
}

View File

@ -1,6 +1,7 @@
package worker package worker
import ( import (
"fmt"
"elyby/minecraft-skinsystem/lib/data" "elyby/minecraft-skinsystem/lib/data"
"elyby/minecraft-skinsystem/lib/services" "elyby/minecraft-skinsystem/lib/services"
) )
@ -18,13 +19,23 @@ func handleChangeUsername(model usernameChanged) (bool) {
return true return true
} }
record, err := data.FindSkinByUsername(model.OldUsername) record, err := data.FindSkinById(model.AccountId)
if (err != nil) { if (err != nil) {
services.Logger.IncCounter("worker.change_username.username_not_found", 1) services.Logger.IncCounter("worker.change_username.id_not_found", 1)
// TODO: я не уверен, что это валидное поведение fmt.Println("Cannot find user id. Trying to search.")
// Суть в том, что здесь может возникнуть ошибка в том случае, если записи в базе нету response, err := getById(model.AccountId)
// а значит его нужно, как минимум, зарегистрировать if err != nil {
return true 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.Username = model.NewUsername
@ -35,11 +46,23 @@ func handleChangeUsername(model usernameChanged) (bool) {
return true return true
} }
func handleSkinChanged(model skinChanged) (bool) { func handleSkinChanged(model skinChanged) bool {
record, err := data.FindSkinById(model.AccountId) record, err := data.FindSkinById(model.AccountId)
if (err != nil) { if err != nil {
services.Logger.IncCounter("worker.skin_changed.id_not_found", 1) services.Logger.IncCounter("worker.skin_changed.id_not_found", 1)
return true 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.Uuid = model.Uuid

58
lib/worker/supports.go Normal file
View File

@ -0,0 +1,58 @@
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 {
tempToken, err := accounts.GetToken(*AccountsTokenConfig)
if err != nil {
return &accounts.Token{}, err
}
token = &tempToken
}
return token, nil
}
func resetRepeatsCount() {
repeatsCount = 0
}

View File

@ -19,6 +19,7 @@ import (
"elyby/minecraft-skinsystem/lib/routes" "elyby/minecraft-skinsystem/lib/routes"
"elyby/minecraft-skinsystem/lib/services" "elyby/minecraft-skinsystem/lib/services"
"elyby/minecraft-skinsystem/lib/worker" "elyby/minecraft-skinsystem/lib/worker"
"elyby/minecraft-skinsystem/lib/external/accounts"
) )
const redisPoolSize int = 10 const redisPoolSize int = 10
@ -28,6 +29,20 @@ func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) 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") log.Println("Connecting to redis")
var redisString = os.Getenv("REDIS_ADDR") var redisString = os.Getenv("REDIS_ADDR")
@ -62,6 +77,7 @@ func main() {
// statsd // statsd
var statsdString = os.Getenv("STATSD_ADDR") var statsdString = os.Getenv("STATSD_ADDR")
statsdString = ""
if (statsdString != "") { if (statsdString != "") {
log.Println("Connecting to statsd") log.Println("Connecting to statsd")
hostname, _ := os.Hostname() hostname, _ := os.Hostname()