mirror of
https://github.com/elyby/chrly.git
synced 2025-01-09 13:27:49 +05:30
Merge branch 'develop'
This commit is contained in:
commit
6a54af62aa
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Игнорим данные, т.к. они не нужны для внутреннего содержимого этого контейнера
|
||||||
|
data
|
@ -1,6 +1,9 @@
|
|||||||
FROM golang:1.7
|
FROM golang:1.7-alpine
|
||||||
|
|
||||||
|
RUN apk add --no-cache git
|
||||||
|
|
||||||
RUN mkdir -p /go/src/elyby/minecraft-skinsystem \
|
RUN mkdir -p /go/src/elyby/minecraft-skinsystem \
|
||||||
|
/go/src/elyby/minecraft-skinsystem/data/capes \
|
||||||
&& ln -s /go/src/elyby/minecraft-skinsystem /go/src/app
|
&& ln -s /go/src/elyby/minecraft-skinsystem /go/src/app
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
WORKDIR /go/src/app
|
||||||
|
2
data/capes/.gitignore
vendored
Normal file
2
data/capes/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
@ -1,10 +1,13 @@
|
|||||||
version: '2'
|
version: '2'
|
||||||
services:
|
services:
|
||||||
app:
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:3.0
|
image: redis:3.0-alpine
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/redis:/data
|
- ./data/redis:/data
|
||||||
|
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq:3.6
|
||||||
|
environment:
|
||||||
|
RABBITMQ_DEFAULT_USER: "ely-skinsystem-app"
|
||||||
|
RABBITMQ_DEFAULT_PASS: "ely-skinsystem-app-password"
|
||||||
|
RABBITMQ_DEFAULT_VHOST: "/ely"
|
||||||
|
@ -1,17 +1,23 @@
|
|||||||
version: '2'
|
version: '2'
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
extends:
|
|
||||||
file: docker-compose.base.yml
|
|
||||||
service: app
|
|
||||||
build: .
|
build: .
|
||||||
|
image: registry.ely.by/elyby/skinsystem:latest
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/go/src/app
|
- ./:/go/src/app
|
||||||
command: ["go", "run", "minecraft-skinsystem.go"]
|
command: ["go", "run", "minecraft-skinsystem.go"]
|
||||||
links:
|
links:
|
||||||
- redis
|
- redis
|
||||||
|
- rabbitmq
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
extends:
|
extends:
|
||||||
file: docker-compose.base.yml
|
file: docker-compose.base.yml
|
||||||
service: redis
|
service: redis
|
||||||
|
|
||||||
|
rabbitmq:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.base.yml
|
||||||
|
service: rabbitmq
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
version: '2'
|
version: '2'
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
extends:
|
image: registry.ely.by/elyby/skinsystem:latest
|
||||||
file: docker-compose.base.yml
|
ports:
|
||||||
service: app
|
- "80:80"
|
||||||
image: erickskrauch/ely-by-skinsystem:master
|
|
||||||
links:
|
links:
|
||||||
- redis
|
- redis
|
||||||
|
- rabbitmq
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
@ -14,3 +14,9 @@ services:
|
|||||||
file: docker-compose.base.yml
|
file: docker-compose.base.yml
|
||||||
service: redis
|
service: redis
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
|
rabbitmq:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.base.yml
|
||||||
|
service: rabbitmq
|
||||||
|
restart: always
|
||||||
|
43
lib/data/CapeItem.go
Normal file
43
lib/data/CapeItem.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"elyby/minecraft-skinsystem/lib/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CapeItem struct {
|
||||||
|
File *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindCapeByUsername(username string) (CapeItem, error) {
|
||||||
|
var record CapeItem
|
||||||
|
file, err := os.Open(services.RootFolder + "/data/capes/" + strings.ToLower(username) + ".png")
|
||||||
|
if (err != nil) {
|
||||||
|
return record, CapeNotFound{username}
|
||||||
|
}
|
||||||
|
|
||||||
|
record.File = file
|
||||||
|
|
||||||
|
return record, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cape *CapeItem) CalculateHash() string {
|
||||||
|
hasher := md5.New()
|
||||||
|
io.Copy(hasher, cape.File)
|
||||||
|
|
||||||
|
return hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
type CapeNotFound struct {
|
||||||
|
Who string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e CapeNotFound) Error() string {
|
||||||
|
return fmt.Sprintf("Cape file not found. Required username \"%v\"", e.Who)
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type DataNotFound struct {
|
|
||||||
Who string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DataNotFound) Error() string {
|
|
||||||
return fmt.Sprintf("Skin data not found. Required username \"%v\"", e.Who)
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ package data
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"fmt"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"elyby/minecraft-skinsystem/lib/services"
|
"elyby/minecraft-skinsystem/lib/services"
|
||||||
@ -11,25 +12,59 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SkinItem struct {
|
type SkinItem struct {
|
||||||
UserId int `json:"userId"`
|
UserId int `json:"userId"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
SkinId int `json:"skinId"`
|
SkinId int `json:"skinId"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Is1_8 bool `json:"is1_8"`
|
Is1_8 bool `json:"is1_8"`
|
||||||
IsSlim bool `json:"isSlim"`
|
IsSlim bool `json:"isSlim"`
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
|
oldUsername string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const accountIdToUsernameKey string = "hash:username-to-account-id"
|
||||||
|
|
||||||
func (s *SkinItem) Save() {
|
func (s *SkinItem) Save() {
|
||||||
str, _ := json.Marshal(s)
|
str, _ := json.Marshal(s)
|
||||||
services.RedisPool.Cmd("SET", tools.BuildKey(s.Username), str)
|
pool, _ := services.RedisPool.Get()
|
||||||
|
pool.Cmd("MULTI")
|
||||||
|
|
||||||
|
// Если пользователь сменил ник, то мы должны удать его ключ
|
||||||
|
if (s.oldUsername != "" && s.oldUsername != s.Username) {
|
||||||
|
pool.Cmd("DEL", tools.BuildKey(s.oldUsername))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если это новая запись или если пользователь сменил ник, то обновляем значение в хэш-таблице
|
||||||
|
if (s.oldUsername != "" || s.oldUsername != s.Username) {
|
||||||
|
pool.Cmd("HSET", accountIdToUsernameKey, s.UserId, s.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.Cmd("SET", tools.BuildKey(s.Username), str)
|
||||||
|
|
||||||
|
pool.Cmd("EXEC")
|
||||||
|
|
||||||
|
s.oldUsername = s.Username
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindRecord(username string) (SkinItem, error) {
|
func (s *SkinItem) Delete() {
|
||||||
|
if (s.oldUsername == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pool, _ := services.RedisPool.Get()
|
||||||
|
pool.Cmd("MULTI")
|
||||||
|
|
||||||
|
pool.Cmd("DEL", tools.BuildKey(s.oldUsername))
|
||||||
|
pool.Cmd("HDEL", accountIdToUsernameKey, s.UserId)
|
||||||
|
|
||||||
|
pool.Cmd("EXEC")
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindSkinByUsername(username string) (SkinItem, error) {
|
||||||
var record SkinItem;
|
var record SkinItem;
|
||||||
response := services.RedisPool.Cmd("GET", tools.BuildKey(username));
|
response := services.RedisPool.Cmd("GET", tools.BuildKey(username));
|
||||||
if (response.IsType(redis.Nil)) {
|
if (response.IsType(redis.Nil)) {
|
||||||
return record, DataNotFound{username}
|
return record, SkinNotFound{username}
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := response.Str()
|
result, err := response.Str()
|
||||||
@ -38,7 +73,28 @@ func FindRecord(username string) (SkinItem, error) {
|
|||||||
if (decodeErr != nil) {
|
if (decodeErr != nil) {
|
||||||
log.Println("Cannot decode record data")
|
log.Println("Cannot decode record data")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record.oldUsername = record.Username
|
||||||
}
|
}
|
||||||
|
|
||||||
return record, err
|
return record, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FindSkinById(id int) (SkinItem, error) {
|
||||||
|
response := services.RedisPool.Cmd("HGET", accountIdToUsernameKey, id);
|
||||||
|
if (response.IsType(redis.Nil)) {
|
||||||
|
return SkinItem{}, SkinNotFound{"unknown"}
|
||||||
|
}
|
||||||
|
|
||||||
|
username, _ := response.Str()
|
||||||
|
|
||||||
|
return FindSkinByUsername(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SkinNotFound struct {
|
||||||
|
Who string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SkinNotFound) Error() string {
|
||||||
|
return fmt.Sprintf("Skin data not found. Required username \"%v\"", e.Who)
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package data
|
|||||||
|
|
||||||
type TexturesResponse struct {
|
type TexturesResponse struct {
|
||||||
Skin *Skin `json:"SKIN"`
|
Skin *Skin `json:"SKIN"`
|
||||||
|
Cape *Cape `json:"CAPE,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Skin struct {
|
type Skin struct {
|
||||||
@ -13,3 +14,8 @@ type Skin struct {
|
|||||||
type SkinMetadata struct {
|
type SkinMetadata struct {
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Cape struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"elyby/minecraft-skinsystem/lib/tools"
|
"elyby/minecraft-skinsystem/lib/tools"
|
||||||
|
"elyby/minecraft-skinsystem/lib/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Cape(w http.ResponseWriter, r *http.Request) {
|
func Cape(response http.ResponseWriter, request *http.Request) {
|
||||||
username := tools.ParseUsername(mux.Vars(r)["username"])
|
username := tools.ParseUsername(mux.Vars(request)["username"])
|
||||||
log.Println("request cape for username " + username)
|
log.Println("request cape for username " + username)
|
||||||
http.Redirect(w, r, "http://skins.minecraft.net/MinecraftCloaks/" + username + ".png", 301)
|
rec, err := data.FindCapeByUsername(username)
|
||||||
|
if (err != nil) {
|
||||||
|
http.Redirect(response, request, "http://skins.minecraft.net/MinecraftCloaks/" + username + ".png", 301)
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Header.Set("Content-Type", "image/png")
|
||||||
|
io.Copy(response, rec.File)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CapeGET(w http.ResponseWriter, r *http.Request) {
|
func CapeGET(w http.ResponseWriter, r *http.Request) {
|
||||||
|
31
lib/routes/Face.go
Normal file
31
lib/routes/Face.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"elyby/minecraft-skinsystem/lib/tools"
|
||||||
|
"elyby/minecraft-skinsystem/lib/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
hash = defaultHash;
|
||||||
|
} else {
|
||||||
|
hash = rec.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, tools.BuildElyUrl(buildFaceUrl(hash)), 301);
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildFaceUrl(hash string) string {
|
||||||
|
return "/minecfaft/skin_buffer/faces/" + hash + ".png"
|
||||||
|
}
|
@ -13,7 +13,7 @@ import (
|
|||||||
func Skin(w http.ResponseWriter, r *http.Request) {
|
func Skin(w http.ResponseWriter, r *http.Request) {
|
||||||
username := tools.ParseUsername(mux.Vars(r)["username"])
|
username := tools.ParseUsername(mux.Vars(r)["username"])
|
||||||
log.Println("request skin for username " + username);
|
log.Println("request skin for username " + username);
|
||||||
rec, err := data.FindRecord(username)
|
rec, err := data.FindSkinByUsername(username)
|
||||||
if (err != nil) {
|
if (err != nil) {
|
||||||
http.Redirect(w, r, "http://skins.minecraft.net/MinecraftSkins/" + username + ".png", 301)
|
http.Redirect(w, r, "http://skins.minecraft.net/MinecraftSkins/" + username + ".png", 301)
|
||||||
return
|
return
|
||||||
|
@ -9,13 +9,14 @@ import (
|
|||||||
|
|
||||||
"elyby/minecraft-skinsystem/lib/data"
|
"elyby/minecraft-skinsystem/lib/data"
|
||||||
"elyby/minecraft-skinsystem/lib/tools"
|
"elyby/minecraft-skinsystem/lib/tools"
|
||||||
|
"elyby/minecraft-skinsystem/lib/services"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Textures(w http.ResponseWriter, r *http.Request) {
|
func Textures(w http.ResponseWriter, r *http.Request) {
|
||||||
username := tools.ParseUsername(mux.Vars(r)["username"])
|
username := tools.ParseUsername(mux.Vars(r)["username"])
|
||||||
log.Println("request textures for username " + username)
|
log.Println("request textures for username " + username)
|
||||||
|
|
||||||
rec, err := data.FindRecord(username)
|
rec, err := data.FindSkinByUsername(username)
|
||||||
if (err != nil || rec.SkinId == 0) {
|
if (err != nil || rec.SkinId == 0) {
|
||||||
rec.Url = "http://skins.minecraft.net/MinecraftSkins/" + username + ".png"
|
rec.Url = "http://skins.minecraft.net/MinecraftSkins/" + username + ".png"
|
||||||
rec.Hash = string(tools.BuildNonElyTexturesHash(username))
|
rec.Hash = string(tools.BuildNonElyTexturesHash(username))
|
||||||
@ -36,6 +37,24 @@ func Textures(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
capeRec, err := data.FindCapeByUsername(username)
|
||||||
|
if (err == nil) {
|
||||||
|
capeUrl, err := services.Router.Get("cloaks").URL("username", username)
|
||||||
|
if (err != nil) {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var scheme string = "http://";
|
||||||
|
if (r.TLS != nil) {
|
||||||
|
scheme = "https://"
|
||||||
|
}
|
||||||
|
|
||||||
|
textures.Cape = &data.Cape{
|
||||||
|
Url: scheme + r.Host + capeUrl.String(),
|
||||||
|
Hash: capeRec.CalculateHash(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
response,_ := json.Marshal(textures)
|
response,_ := json.Marshal(textures)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Write(response)
|
w.Write(response)
|
||||||
|
@ -2,9 +2,14 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mediocregopher/radix.v2/pool"
|
"github.com/mediocregopher/radix.v2/pool"
|
||||||
|
"github.com/streadway/amqp"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var Router *mux.Router
|
||||||
|
|
||||||
var RedisPool *pool.Pool
|
var RedisPool *pool.Pool
|
||||||
|
|
||||||
var Router *mux.Router
|
var RabbitMQChannel *amqp.Channel
|
||||||
|
|
||||||
|
var RootFolder string
|
||||||
|
52
lib/worker/handlers.go
Normal file
52
lib/worker/handlers.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package worker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"elyby/minecraft-skinsystem/lib/data"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleChangeUsername(model usernameChanged) (bool) {
|
||||||
|
if (model.OldUsername == "") {
|
||||||
|
record := data.SkinItem{
|
||||||
|
UserId: model.AccountId,
|
||||||
|
Username: model.NewUsername,
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Save()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
record, err := data.FindSkinByUsername(model.OldUsername)
|
||||||
|
if (err != nil) {
|
||||||
|
log.Println("Exit by not found record")
|
||||||
|
// TODO: я не уверен, что это валидное поведение
|
||||||
|
// Суть в том, что здесь может возникнуть ошибка в том случае, если записи в базе нету
|
||||||
|
// а значит его нужно, как минимум, зарегистрировать
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Username = model.NewUsername
|
||||||
|
record.Save()
|
||||||
|
|
||||||
|
log.Println("all saved!")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSkinChanged(model skinChanged) (bool) {
|
||||||
|
record, err := data.FindSkinById(model.AccountId)
|
||||||
|
if (err != nil) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
record.SkinId = model.SkinId
|
||||||
|
record.Hash = model.Hash
|
||||||
|
record.Is1_8 = model.Is1_8
|
||||||
|
record.IsSlim = model.IsSlim
|
||||||
|
record.Url = model.Url
|
||||||
|
|
||||||
|
record.Save()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
17
lib/worker/models.go
Normal file
17
lib/worker/models.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package worker
|
||||||
|
|
||||||
|
type usernameChanged struct {
|
||||||
|
AccountId int `json:"accountId"`
|
||||||
|
OldUsername string `json:"oldUsername"`
|
||||||
|
NewUsername string `json:"newUsername"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
88
lib/worker/worker.go
Normal file
88
lib/worker/worker.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package worker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"elyby/minecraft-skinsystem/lib/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
const exchangeName string = "events"
|
||||||
|
const queueName string = "skinsystem-accounts-events"
|
||||||
|
|
||||||
|
func Listen() {
|
||||||
|
var err error
|
||||||
|
ch := services.RabbitMQChannel
|
||||||
|
|
||||||
|
err = ch.ExchangeDeclare(
|
||||||
|
exchangeName, // name
|
||||||
|
"topic", // type
|
||||||
|
true, // durable
|
||||||
|
false, // auto-deleted
|
||||||
|
false, // internal
|
||||||
|
false, // no-wait
|
||||||
|
nil, // arguments
|
||||||
|
)
|
||||||
|
failOnError(err, "Failed to declare an exchange")
|
||||||
|
|
||||||
|
_, err = ch.QueueDeclare(
|
||||||
|
queueName, // name
|
||||||
|
true, // durable
|
||||||
|
false, // delete when usused
|
||||||
|
false, // exclusive
|
||||||
|
false, // no-wait
|
||||||
|
nil, // arguments
|
||||||
|
)
|
||||||
|
failOnError(err, "Failed to declare a queue")
|
||||||
|
|
||||||
|
err = ch.QueueBind(queueName, "accounts.username-changed", exchangeName, false, nil)
|
||||||
|
failOnError(err, "Failed to bind a queue")
|
||||||
|
|
||||||
|
err = ch.QueueBind(queueName, "accounts.skin-changed", exchangeName, false, nil)
|
||||||
|
failOnError(err, "Failed to bind a queue")
|
||||||
|
|
||||||
|
msgs, err := ch.Consume(
|
||||||
|
queueName, // queue
|
||||||
|
"", // consumer
|
||||||
|
false, // auto-ack
|
||||||
|
false, // exclusive
|
||||||
|
false, // no-local
|
||||||
|
false, // no-wait
|
||||||
|
nil, // args
|
||||||
|
)
|
||||||
|
failOnError(err, "Failed to register a consumer")
|
||||||
|
|
||||||
|
forever := make(chan bool)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for d := range msgs {
|
||||||
|
log.Println("Incoming message with routing key " + d.RoutingKey)
|
||||||
|
var result bool = true;
|
||||||
|
switch d.RoutingKey {
|
||||||
|
case "accounts.username-changed":
|
||||||
|
var model usernameChanged
|
||||||
|
json.Unmarshal(d.Body, &model)
|
||||||
|
result = handleChangeUsername(model)
|
||||||
|
case "accounts.skin-changed":
|
||||||
|
var model skinChanged
|
||||||
|
json.Unmarshal(d.Body, &model)
|
||||||
|
result = handleSkinChanged(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
d.Ack(false)
|
||||||
|
} else {
|
||||||
|
d.Reject(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-forever
|
||||||
|
}
|
||||||
|
|
||||||
|
func failOnError(err error, msg string) {
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("%s: %s", msg, err)
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,23 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"log"
|
"log"
|
||||||
"runtime"
|
"runtime"
|
||||||
//"time"
|
"time"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/streadway/amqp"
|
||||||
"github.com/mediocregopher/radix.v2/pool"
|
"github.com/mediocregopher/radix.v2/pool"
|
||||||
|
|
||||||
"elyby/minecraft-skinsystem/lib/routes"
|
"elyby/minecraft-skinsystem/lib/routes"
|
||||||
"elyby/minecraft-skinsystem/lib/services"
|
"elyby/minecraft-skinsystem/lib/services"
|
||||||
//"github.com/mediocregopher/radix.v2/redis"
|
"elyby/minecraft-skinsystem/lib/worker"
|
||||||
)
|
)
|
||||||
|
|
||||||
const redisString string = "redis:6379"
|
const redisPoolSize int = 10
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Println("Starting...")
|
log.Println("Starting...")
|
||||||
@ -22,16 +25,43 @@ func main() {
|
|||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
log.Println("Connecting to redis")
|
log.Println("Connecting to redis")
|
||||||
redisPool, redisErr := pool.New("tcp", redisString, 10)
|
|
||||||
if redisErr != nil {
|
var redisString = os.Getenv("REDIS_ADDR")
|
||||||
|
if (redisString == "") {
|
||||||
|
redisString = "redis:6379"
|
||||||
|
}
|
||||||
|
|
||||||
|
redisPool, redisErr := pool.New("tcp", redisString, redisPoolSize)
|
||||||
|
if (redisErr != nil) {
|
||||||
log.Fatal("Redis unavailable")
|
log.Fatal("Redis unavailable")
|
||||||
}
|
}
|
||||||
log.Println("Connected to redis")
|
log.Println("Connected to redis")
|
||||||
|
|
||||||
|
log.Println("Connecting to rabbitmq")
|
||||||
|
// TODO: rabbitmq становится доступен не сразу. Нужно дождаться, пока он станет доступен, периодически повторяя запросы
|
||||||
|
|
||||||
|
var rabbitmqString = os.Getenv("RABBITMQ_ADDR")
|
||||||
|
if (rabbitmqString == "") {
|
||||||
|
rabbitmqString = "amqp://ely-skinsystem-app:ely-skinsystem-app-password@rabbitmq:5672/%2fely"
|
||||||
|
}
|
||||||
|
|
||||||
|
rabbitConnection, rabbitmqErr := amqp.Dial(rabbitmqString)
|
||||||
|
if (rabbitmqErr != nil) {
|
||||||
|
log.Fatalf("%s", rabbitmqErr)
|
||||||
|
}
|
||||||
|
log.Println("Connected to rabbitmq. Trying to open a channel")
|
||||||
|
rabbitChannel, rabbitmqErr := rabbitConnection.Channel()
|
||||||
|
if (rabbitmqErr != nil) {
|
||||||
|
log.Fatalf("%s", rabbitmqErr)
|
||||||
|
}
|
||||||
|
log.Println("Connected to rabbitmq channel")
|
||||||
|
|
||||||
router := mux.NewRouter().StrictSlash(true)
|
router := mux.NewRouter().StrictSlash(true)
|
||||||
router.HandleFunc("/skins/{username}", routes.Skin).Methods("GET").Name("skins")
|
router.HandleFunc("/skins/{username}", routes.Skin).Methods("GET").Name("skins")
|
||||||
router.HandleFunc("/cloaks/{username}", routes.Cape).Methods("GET").Name("cloaks")
|
router.HandleFunc("/cloaks/{username}", routes.Cape).Methods("GET").Name("cloaks")
|
||||||
router.HandleFunc("/textures/{username}", routes.Textures).Methods("GET").Name("textures")
|
router.HandleFunc("/textures/{username}", routes.Textures).Methods("GET").Name("textures")
|
||||||
|
router.HandleFunc("/skins/{username}/face", routes.Face).Methods("GET").Name("faces")
|
||||||
|
router.HandleFunc("/skins/{username}/face.png", routes.Face).Methods("GET").Name("faces")
|
||||||
// Legacy
|
// Legacy
|
||||||
router.HandleFunc("/minecraft.php", routes.MinecraftPHP).Methods("GET")
|
router.HandleFunc("/minecraft.php", routes.MinecraftPHP).Methods("GET")
|
||||||
router.HandleFunc("/skins/", routes.SkinGET).Methods("GET")
|
router.HandleFunc("/skins/", routes.SkinGET).Methods("GET")
|
||||||
@ -42,26 +72,36 @@ func main() {
|
|||||||
apiRouter := router.PathPrefix("/api").Subrouter()
|
apiRouter := router.PathPrefix("/api").Subrouter()
|
||||||
apiRouter.HandleFunc("/user/{username}/skin", routes.SetSkin).Methods("POST")
|
apiRouter.HandleFunc("/user/{username}/skin", routes.SetSkin).Methods("POST")
|
||||||
|
|
||||||
services.RedisPool = redisPool
|
|
||||||
services.Router = router
|
services.Router = router
|
||||||
|
services.RedisPool = redisPool
|
||||||
|
services.RabbitMQChannel = rabbitChannel
|
||||||
|
|
||||||
/*go func() {
|
_, file, _, _ := runtime.Caller(0)
|
||||||
|
services.RootFolder = filepath.Dir(file)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
period := 5
|
||||||
for {
|
for {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(time.Duration(period) * time.Second)
|
||||||
|
|
||||||
resp := services.Redis.Cmd("PING")
|
resp := services.RedisPool.Cmd("PING")
|
||||||
if (resp.Err != nil) {
|
if (resp.Err == nil) {
|
||||||
log.Println("Redis not pinged. Try to reconnect")
|
// Если редис успешно пинганулся, значит всё хорошо
|
||||||
newClient, redisErr := redis.Dial("tcp", redisString)
|
continue
|
||||||
if (redisErr != nil) {
|
}
|
||||||
log.Println("Cannot reconnect to redis")
|
|
||||||
} else {
|
log.Println("Redis not pinged. Try to reconnect")
|
||||||
services.Redis = newClient
|
newPool, redisErr := pool.New("tcp", redisString, redisPoolSize)
|
||||||
log.Println("Reconnected")
|
if (redisErr != nil) {
|
||||||
}
|
log.Printf("Cannot reconnect to redis, waiting %d seconds\n", period)
|
||||||
|
} else {
|
||||||
|
services.RedisPool = newPool
|
||||||
|
log.Println("Reconnected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()*/
|
}()
|
||||||
|
|
||||||
|
go worker.Listen()
|
||||||
|
|
||||||
log.Println("Started");
|
log.Println("Started");
|
||||||
log.Fatal(http.ListenAndServe(":80", router))
|
log.Fatal(http.ListenAndServe(":80", router))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user