mirror of
https://github.com/elyby/chrly.git
synced 2024-11-30 10:42:14 +05:30
Перенесена имплементация на Go, описано Docker compose окружение
This commit is contained in:
parent
b38b78bd1e
commit
baa1cd3010
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
|
# IDEA
|
||||||
/.idea
|
/.idea
|
||||||
|
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
FROM golang:1.7
|
||||||
|
|
||||||
|
RUN mkdir -p /go/src/app
|
||||||
|
WORKDIR /go/src/app
|
||||||
|
|
||||||
|
COPY ./src /go/src/app
|
||||||
|
|
||||||
|
RUN go-wrapper download
|
||||||
|
RUN go-wrapper install
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
VOLUME ["/go/src/app"]
|
||||||
|
|
||||||
|
CMD ["go-wrapper", "run"]
|
2
data/.gitignore
vendored
Normal file
2
data/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
20
docker-compose.yml
Normal file
20
docker-compose.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
links:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
web:
|
||||||
|
build: ./docker/nginx
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
links:
|
||||||
|
- app
|
||||||
|
volumes_from:
|
||||||
|
- app
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:3.0
|
||||||
|
volumes:
|
||||||
|
- ./data/:/data
|
8
docker/nginx/Dockerfile
Normal file
8
docker/nginx/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
FROM nginx:1.11
|
||||||
|
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY skinsystem.ely.by.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
RUN mkdir -p /data/cache \
|
||||||
|
/data/logs \
|
||||||
|
&& chown nginx:nginx -R /data
|
31
docker/nginx/nginx.conf
Normal file
31
docker/nginx/nginx.conf
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 4048;
|
||||||
|
multi_accept on;
|
||||||
|
use epoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
keepalive_timeout 15;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
access_log /data/logs/access.log combined buffer=32k;
|
||||||
|
error_log /data/logs/error.log crit;
|
||||||
|
|
||||||
|
gzip off;
|
||||||
|
|
||||||
|
client_max_body_size 1M;
|
||||||
|
|
||||||
|
proxy_cache_path /data/cache levels=1:2 keys_zone=cache:30m max_size=1G;
|
||||||
|
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
}
|
63
docker/nginx/skinsystem.ely.by.conf
Normal file
63
docker/nginx/skinsystem.ely.by.conf
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
charset utf-8;
|
||||||
|
set $root_path '/go/src/app';
|
||||||
|
root $root_path;
|
||||||
|
|
||||||
|
proxy_cache_use_stale error timeout invalid_header http_502;
|
||||||
|
proxy_cache_min_uses 1;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://app;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
|
||||||
|
proxy_cache cache;
|
||||||
|
proxy_cache_valid 30m;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /minecraft.php {
|
||||||
|
if ($arg_name = "") {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($arg_type = "cloack") {
|
||||||
|
rewrite .* http://skins.minecraft.net/MinecraftCloaks/$arg_name.png? permanent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($arg_type = "skin") {
|
||||||
|
rewrite .* /skins/$arg_name last;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /cloaks/ {
|
||||||
|
try_files $uri $uri.png @cloaks;
|
||||||
|
}
|
||||||
|
|
||||||
|
location @cloaks {
|
||||||
|
rewrite ^/cloaks/(.+?)(\.[^.]*$|$)$ http://skins.minecraft.net/MinecraftCloaks/$1.png? permanent;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* ^/skins/$ {
|
||||||
|
if ($arg_name = "") {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
rewrite .* /skins/$arg_name last;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* ^/cloaks/$ {
|
||||||
|
if ($arg_name = "") {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
rewrite .* /cloaks/$arg_name last;
|
||||||
|
}
|
||||||
|
}
|
40
nginx.conf
40
nginx.conf
@ -1,40 +0,0 @@
|
|||||||
location /minecraft.php {
|
|
||||||
if ($arg_name = "") {
|
|
||||||
return 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($arg_type = "cloack") {
|
|
||||||
rewrite .* http://skins.minecraft.net/MinecraftCloaks/$arg_name.png? permanent;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($arg_type = "skin") {
|
|
||||||
rewrite .* /skins/$arg_name last;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 404;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /cloaks/ {
|
|
||||||
try_files $uri $uri.png @cloaks;
|
|
||||||
}
|
|
||||||
|
|
||||||
location @cloaks {
|
|
||||||
rewrite ^/cloaks/(.+?)(\.[^.]*$|$)$ http://skins.minecraft.net/MinecraftCloaks/$1.png? permanent;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* ^/skins/$ {
|
|
||||||
if ($arg_name = "") {
|
|
||||||
return 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
rewrite .* /skins/$arg_name permanent;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* ^/cloaks/$ {
|
|
||||||
if ($arg_name = "") {
|
|
||||||
return 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
rewrite .* /cloaks/$arg_name permanent;
|
|
||||||
}
|
|
133
src/main.go
Normal file
133
src/main.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user