mirror of
https://github.com/elyby/chrly.git
synced 2024-11-30 10:42:14 +05:30
Merge branch 'develop'
This commit is contained in:
commit
8b51c1bd0c
@ -12,6 +12,10 @@ services:
|
|||||||
- redis
|
- redis
|
||||||
- rabbitmq
|
- rabbitmq
|
||||||
- statsd
|
- statsd
|
||||||
|
environment:
|
||||||
|
ACCOUNTS_API_ID: ""
|
||||||
|
ACCOUNTS_API_SECRET: ""
|
||||||
|
STATSD_ADDR: ""
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
extends:
|
extends:
|
||||||
|
@ -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
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://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}
|
||||||
|
}
|
||||||
|
}
|
@ -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
58
lib/worker/supports.go
Normal 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
|
||||||
|
}
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user