mirror of
https://github.com/azures04/crafatar.git
synced 2026-03-22 07:51:17 +01:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e2a23ccbb | ||
| 41690f84c7 | |||
|
|
d6293cc73d | ||
|
|
c155c8d098 | ||
|
|
bba004acc7 | ||
|
|
9cb32a843f | ||
|
|
e44ebda56f | ||
|
|
fb4d24de6b | ||
|
|
59f27f0769 | ||
|
|
019ca37037 | ||
|
|
56765488e0 | ||
|
|
1328f98746 | ||
|
|
ef4b2f8005 | ||
|
|
fe5ce6b688 | ||
|
|
a6e8e6b0f9 | ||
|
|
29955a1765 | ||
|
|
265a98d404 | ||
|
|
624bf0e338 | ||
|
|
db565f86c8 |
@ -1,2 +0,0 @@
|
|||||||
https://github.com/mojodna/heroku-buildpack-cairo.git
|
|
||||||
https://github.com/heroku/heroku-buildpack-nodejs.git
|
|
||||||
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.*
|
||||||
|
*.md
|
||||||
|
Dockerfile
|
||||||
|
LICENSE
|
||||||
|
images/
|
||||||
|
node_modules/
|
||||||
@ -1,21 +0,0 @@
|
|||||||
# We use EditorConfig to standardize settings between contributors
|
|
||||||
# See http://editorconfig.org for more info and plugin downloads
|
|
||||||
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = false
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.{js, json, yml}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
charset = utf-8
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
||||||
[.gitignore]
|
|
||||||
# echo "filename" >> .gitignorre
|
|
||||||
insert_final_newline = true
|
|
||||||
24
.travis.yml
24
.travis.yml
@ -1,24 +0,0 @@
|
|||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- 12.16.1
|
|
||||||
sudo: false
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- libcairo2-dev
|
|
||||||
- libjpeg8-dev
|
|
||||||
- libpango1.0-dev
|
|
||||||
- libgif-dev
|
|
||||||
- build-essential
|
|
||||||
- g++-4.8
|
|
||||||
script:
|
|
||||||
- npm run-script test-travis
|
|
||||||
env:
|
|
||||||
- TRAVIS=true CXX=g++-4.8
|
|
||||||
services:
|
|
||||||
- redis-server
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- node_modules
|
|
||||||
70
Dockerfile
70
Dockerfile
@ -1,47 +1,35 @@
|
|||||||
FROM node:12-alpine
|
FROM node:12-alpine AS builder
|
||||||
|
|
||||||
ARG AVATAR_MIN
|
RUN apk --no-cache add git python3 build-base redis cairo-dev pango-dev jpeg-dev giflib-dev
|
||||||
ARG AVATAR_MAX
|
|
||||||
ARG AVATAR_DEFAULT
|
|
||||||
ARG RENDER_MIN
|
|
||||||
ARG RENDER_MAX
|
|
||||||
ARG RENDER_DEFAULT
|
|
||||||
ARG FACE_DIR
|
|
||||||
ARG HELM_DIR
|
|
||||||
ARG SKIN_DIR
|
|
||||||
ARG RENDER_DIR
|
|
||||||
ARG CAPE_DIR
|
|
||||||
ARG CACHE_LOCAL
|
|
||||||
ARG CACHE_BROWSER
|
|
||||||
ARG EPHEMERAL_STORAGE
|
|
||||||
ARG REDIS_URL
|
|
||||||
ARG PORT
|
|
||||||
ARG BIND
|
|
||||||
ARG EXTERNAL_HTTP_TIMEOUT
|
|
||||||
ARG DEBUG
|
|
||||||
ARG LOG_TIME
|
|
||||||
ARG SPONSOR_SIDE
|
|
||||||
ARG TOP_RIGHT
|
|
||||||
|
|
||||||
ENV NODE_ENV production
|
RUN adduser -D app
|
||||||
|
USER app
|
||||||
RUN apk --no-cache --virtual .build-deps add git python build-base
|
|
||||||
RUN apk --no-cache --virtual .canvas-deps add cairo-dev pango-dev jpeg-dev giflib-dev
|
|
||||||
|
|
||||||
RUN mkdir -p /crafatar/images/faces
|
|
||||||
RUN mkdir -p /crafatar/images/helms
|
|
||||||
RUN mkdir -p /crafatar/images/skins
|
|
||||||
RUN mkdir -p /crafatar/images/renders
|
|
||||||
RUN mkdir -p /crafatar/images/capes
|
|
||||||
|
|
||||||
VOLUME /crafatar/images
|
|
||||||
|
|
||||||
COPY package.json www.js config.js crafatar/
|
|
||||||
COPY lib/ crafatar/lib/
|
|
||||||
|
|
||||||
WORKDIR /crafatar
|
|
||||||
|
|
||||||
|
COPY --chown=app package.json package-lock.json /home/app/crafatar/
|
||||||
|
WORKDIR /home/app/crafatar
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
|
COPY --chown=app . .
|
||||||
|
RUN mkdir -p images/faces images/helms images/skins images/renders images/capes
|
||||||
|
|
||||||
|
ARG VERBOSE_TEST
|
||||||
|
ARG DEBUG
|
||||||
|
RUN nohup redis-server & npm test
|
||||||
|
|
||||||
|
|
||||||
|
FROM node:12-alpine
|
||||||
|
RUN apk --no-cache add cairo pango jpeg giflib
|
||||||
|
RUN adduser -D app
|
||||||
|
USER app
|
||||||
|
RUN mkdir /home/app/crafatar
|
||||||
|
WORKDIR /home/app/crafatar
|
||||||
|
RUN mkdir -p images/faces images/helms images/skins images/renders images/capes
|
||||||
|
|
||||||
|
COPY --chown=app --from=builder /home/app/crafatar/node_modules/ node_modules/
|
||||||
|
COPY --chown=app package.json www.js config.js ./
|
||||||
|
COPY --chown=app lib/ lib/
|
||||||
|
|
||||||
|
VOLUME /home/app/crafatar/images
|
||||||
|
ENV NODE_ENV production
|
||||||
|
ENTRYPOINT ["npm", "start"]
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENTRYPOINT npm start
|
|
||||||
17
README.md
17
README.md
@ -34,6 +34,14 @@ Please [visit the website](https://crafatar.com) for details.
|
|||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker network create crafatar
|
||||||
|
docker run --net crafatar -d --name redis redis
|
||||||
|
docker run --net crafatar -v crafatar-images:/home/app/crafatar/images -e REDIS_URL=redis://redis -p 3000:3000 crafatar/crafatar
|
||||||
|
```
|
||||||
|
|
||||||
## Manual
|
## Manual
|
||||||
|
|
||||||
- Install [nodejs](https://nodejs.org/) 12 (LTS)
|
- Install [nodejs](https://nodejs.org/) 12 (LTS)
|
||||||
@ -44,15 +52,6 @@ Please [visit the website](https://crafatar.com) for details.
|
|||||||
|
|
||||||
Crafatar is now available at http://0.0.0.0:3000.
|
Crafatar is now available at http://0.0.0.0:3000.
|
||||||
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
```sh
|
|
||||||
docker pull crafatar/crafatar
|
|
||||||
docker network create crafatar
|
|
||||||
docker run --net crafatar -d --name redis redis
|
|
||||||
docker run --net crafatar -v crafatar-images:/crafatar/images -e REDIS_URL=redis://redis -p 3000:3000 crafatar/crafatar
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configration / Environment variables
|
## Configration / Environment variables
|
||||||
|
|
||||||
See the `config.js` file.
|
See the `config.js` file.
|
||||||
|
|||||||
30
app.json
30
app.json
@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Crafatar",
|
|
||||||
"description": "A blazing fast API for Minecraft faces!",
|
|
||||||
"repository": "https://github.com/crafatar/crafatar",
|
|
||||||
"keywords": [
|
|
||||||
"node",
|
|
||||||
"minecraft",
|
|
||||||
"avatar",
|
|
||||||
"redis"
|
|
||||||
],
|
|
||||||
"website": "https://crafatar.com/",
|
|
||||||
"env": {
|
|
||||||
"EPHEMERAL_STORAGE": {
|
|
||||||
"description": "Set to true if your storage is gone after deploying",
|
|
||||||
"required": false,
|
|
||||||
"value": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"addons": [
|
|
||||||
"rediscloud"
|
|
||||||
],
|
|
||||||
"buildpacks": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/mojodna/heroku-buildpack-cairo.git"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/heroku/heroku-buildpack-nodejs.git"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -54,12 +54,16 @@ var config = {
|
|||||||
log_time: process.env.LOG_TIME === "true",
|
log_time: process.env.LOG_TIME === "true",
|
||||||
// rate limit per second for outgoing requests to the Mojang session server
|
// rate limit per second for outgoing requests to the Mojang session server
|
||||||
// requests exceeding this limit are skipped and considered failed
|
// requests exceeding this limit are skipped and considered failed
|
||||||
sessions_rate_limit: parseInt(process.env.SESSIONS_RATE_LIMIT) || Infinity
|
sessions_rate_limit: parseInt(process.env.SESSIONS_RATE_LIMIT)
|
||||||
},
|
},
|
||||||
sponsor: {
|
sponsor: {
|
||||||
sidebar: process.env.SPONSOR_SIDE,
|
sidebar: process.env.SPONSOR_SIDE,
|
||||||
top_right: process.env.SPONSOR_TOP_RIGHT
|
top_right: process.env.SPONSOR_TOP_RIGHT
|
||||||
},
|
},
|
||||||
|
endpoints: {
|
||||||
|
textures_url: process.env.TEXTURES_ENDPOINT || "https://textures.minecraft.net/texture/",
|
||||||
|
session_url: process.env.SESSION_ENDPOINT || "https://sessionserver.mojang.com/session/minecraft/profile/"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
@ -7,8 +7,8 @@ var skins = require("./skins");
|
|||||||
var path = require("path");
|
var path = require("path");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
|
|
||||||
// 0098cb60-fa8e-427c-b299-793cbd302c9a
|
// 0098cb60fa8e427cb299793cbd302c9a
|
||||||
var valid_user_id = /^[0-9a-fA-F\-]{32,36}$/; // uuid
|
var valid_user_id = /^[0-9a-fA-F]{32}$/; // uuid
|
||||||
var hash_pattern = /[0-9a-f]+$/;
|
var hash_pattern = /[0-9a-f]+$/;
|
||||||
|
|
||||||
// gets the hash from the textures.minecraft.net +url+
|
// gets the hash from the textures.minecraft.net +url+
|
||||||
@ -122,6 +122,14 @@ var requests = {
|
|||||||
cape: {}
|
cape: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var loginterval = setInterval(function(){
|
||||||
|
var skinreqs = Object.keys(requests.skin).length;
|
||||||
|
var capereqs = Object.keys(requests.cape).length;
|
||||||
|
if (skinreqs || capereqs) {
|
||||||
|
logging.log("Currently waiting for " + skinreqs + " skin requests and " + capereqs + " cape requests.");
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
// add a request for +userId+ and +type+ to the queue
|
// add a request for +userId+ and +type+ to the queue
|
||||||
function push_request(userId, type, callback) {
|
function push_request(userId, type, callback) {
|
||||||
// avoid special properties (e.g. 'constructor')
|
// avoid special properties (e.g. 'constructor')
|
||||||
@ -243,7 +251,7 @@ exp.get_image_hash = function(rid, userId, type, callback) {
|
|||||||
// an error occured, but we have a cached hash
|
// an error occured, but we have a cached hash
|
||||||
// (e.g. Mojang servers not reachable, using outdated hash)
|
// (e.g. Mojang servers not reachable, using outdated hash)
|
||||||
|
|
||||||
// when hitting the rate limit, let's pretend the request succeeded and bump the TTL
|
// bump the TTL after hitting the rate limit
|
||||||
var ratelimited = store_err.code === "RATELIMIT";
|
var ratelimited = store_err.code === "RATELIMIT";
|
||||||
cache.update_timestamp(rid, userId, !ratelimited, function(err2) {
|
cache.update_timestamp(rid, userId, !ratelimited, function(err2) {
|
||||||
callback(err2 || store_err, 4, cache_details && cached_hash, slim);
|
callback(err2 || store_err, 4, cache_details && cached_hash, slim);
|
||||||
@ -324,7 +332,7 @@ function get_type(overlay, body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handles creations of 3D renders
|
// handles creations of 3D renders
|
||||||
// callback: error, skin hash, image buffer
|
// callback: error, status, skin hash, image buffer
|
||||||
exp.get_render = function(rid, userId, scale, overlay, body, callback) {
|
exp.get_render = function(rid, userId, scale, overlay, body, callback) {
|
||||||
exp.get_skin(rid, userId, function(err, skin_hash, status, img, slim) {
|
exp.get_skin(rid, userId, function(err, skin_hash, status, img, slim) {
|
||||||
if (!skin_hash) {
|
if (!skin_hash) {
|
||||||
@ -350,7 +358,7 @@ exp.get_render = function(rid, userId, scale, overlay, body, callback) {
|
|||||||
callback(null, 0, skin_hash, null);
|
callback(null, 0, skin_hash, null);
|
||||||
} else {
|
} else {
|
||||||
fs.writeFile(renderpath, drawn_img, "binary", function(write_err) {
|
fs.writeFile(renderpath, drawn_img, "binary", function(write_err) {
|
||||||
callback(write_err, 2, skin_hash, drawn_img);
|
callback(write_err, status, skin_hash, drawn_img);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -387,4 +395,8 @@ exp.get_cape = function(rid, userId, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exp.stoplog = function() {
|
||||||
|
clearInterval(loginterval);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = exp;
|
module.exports = exp;
|
||||||
@ -5,8 +5,8 @@ var skins = require("./skins");
|
|||||||
var http = require("http");
|
var http = require("http");
|
||||||
require("./object-patch");
|
require("./object-patch");
|
||||||
|
|
||||||
var session_url = "https://sessionserver.mojang.com/session/minecraft/profile/";
|
var session_url = config.endpoints.session_url;
|
||||||
var textures_url = "https://textures.minecraft.net/texture/";
|
var textures_url = config.endpoints.textures_url;
|
||||||
|
|
||||||
// count requests made to session_url in the last 1000ms
|
// count requests made to session_url in the last 1000ms
|
||||||
var session_requests = [];
|
var session_requests = [];
|
||||||
@ -23,7 +23,7 @@ function req_count() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// deletes all entries in session_requests older than a second
|
// deletes all entries in session_requests, should be called every 1000ms
|
||||||
exp.resetCounter = function() {
|
exp.resetCounter = function() {
|
||||||
var count = req_count();
|
var count = req_count();
|
||||||
if (count) {
|
if (count) {
|
||||||
@ -41,10 +41,10 @@ exp.resetCounter = function() {
|
|||||||
// callback: the body, response,
|
// callback: the body, response,
|
||||||
// and error buffer. get_from helper method is available
|
// and error buffer. get_from helper method is available
|
||||||
exp.get_from_options = function(rid, url, options, callback) {
|
exp.get_from_options = function(rid, url, options, callback) {
|
||||||
var session_req = url.startsWith(session_url);
|
var is_session_req = config.server.sessions_rate_limit && url.startsWith(session_url);
|
||||||
|
|
||||||
// This is to prevent being blocked by CloudFront for exceeding the rate limit
|
// This is to prevent being blocked by CloudFront for exceeding the rate limit
|
||||||
if (session_req && req_count() >= config.server.sessions_rate_limit) {
|
if (is_session_req && req_count() >= config.server.sessions_rate_limit) {
|
||||||
var e = new Error("Skipped, rate limit exceeded");
|
var e = new Error("Skipped, rate limit exceeded");
|
||||||
e.name = "HTTP";
|
e.name = "HTTP";
|
||||||
e.code = "RATELIMIT";
|
e.code = "RATELIMIT";
|
||||||
@ -54,7 +54,7 @@ exp.get_from_options = function(rid, url, options, callback) {
|
|||||||
|
|
||||||
callback(null, response, e);
|
callback(null, response, e);
|
||||||
} else {
|
} else {
|
||||||
session_req && session_requests.push(Date.now());
|
is_session_req && session_requests.push(Date.now());
|
||||||
request.get({
|
request.get({
|
||||||
url: url,
|
url: url,
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
var valid_user_id = /^[0-9a-f-A-F-]{32,36}$/; // uuid
|
var valid_user_id = /^[0-9a-f-A-F-]{32,36}$/; // uuid
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
|
|
||||||
var quotes = [
|
var quotes = [
|
||||||
["Crafatar is the best at what it does.", "Shotbow Network", "https://twitter.com/ShotbowNetwork/status/565201303555829762"],
|
["Crafatar is the best at what it does.", "Shotbow Network", "https://twitter.com/ShotbowNetwork/status/565201303555829762"],
|
||||||
@ -37,25 +36,18 @@ function changeQuote() {
|
|||||||
current_quote = (current_quote + 1) % quotes.length;
|
current_quote = (current_quote + 1) % quotes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
xhr.onload = function() {
|
fetch('https://mc-heads.net/json/mc_status').then(r => r.json()).then(data => {
|
||||||
var response = JSON.parse(xhr.responseText);
|
var textures_err = data.report.skins.status !== "up";
|
||||||
var status = {};
|
var session_err = data.report.session.status !== "up";
|
||||||
response.map(function(elem) {
|
|
||||||
var key = Object.keys(elem)[0];
|
|
||||||
status[key] = elem[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
var textures_err = status["textures.minecraft.net"] !== "green";
|
|
||||||
var session_err = status["sessionserver.mojang.com"] !== "green";
|
|
||||||
|
|
||||||
if (textures_err || session_err) {
|
if (textures_err || session_err) {
|
||||||
var warn = document.createElement("div");
|
var warn = document.createElement("div");
|
||||||
warn.setAttribute("class", "alert alert-warning");
|
warn.setAttribute("class", "alert alert-warning");
|
||||||
warn.setAttribute("role", "alert");
|
warn.setAttribute("role", "alert");
|
||||||
warn.innerHTML = "<h5>Mojang issues</h5> Mojang's servers are having trouble <i>right now</i>, this may affect requests at Crafatar. <small><a href=\"https://help.mojang.com\" target=\"_blank\">check status</a>";
|
warn.innerHTML = "<h5>Mojang issues</h5> Mojang's servers are having trouble <i>right now</i>, this may affect requests at Crafatar. <small><a href=\"https://mc-heads.net/mcstatus\" target=\"_blank\">check status</a>";
|
||||||
document.querySelector("#alerts").appendChild(warn);
|
document.querySelector("#alerts").appendChild(warn);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function(event) {
|
document.addEventListener("DOMContentLoaded", function(event) {
|
||||||
var avatars = document.querySelector("#avatar-wrapper");
|
var avatars = document.querySelector("#avatar-wrapper");
|
||||||
@ -82,7 +74,4 @@ document.addEventListener("DOMContentLoaded", function(event) {
|
|||||||
images[j].src = images[j].dataset.src.replace("$", value);
|
images[j].src = images[j].dataset.src.replace("$", value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.open("GET", "https://status.mojang.com/check", true);
|
|
||||||
xhr.send();
|
|
||||||
});
|
});
|
||||||
@ -14,12 +14,10 @@ function handle_default(img_status, userId, size, def, req, err, callback) {
|
|||||||
if (defname !== "steve" && defname !== "mhf_steve" && defname !== "alex" && defname !== "mhf_alex") {
|
if (defname !== "steve" && defname !== "mhf_steve" && defname !== "alex" && defname !== "mhf_alex") {
|
||||||
if (helpers.id_valid(def)) {
|
if (helpers.id_valid(def)) {
|
||||||
// clean up the old URL to match new image
|
// clean up the old URL to match new image
|
||||||
var parsed = req.url;
|
req.url.searchParams.delete('default');
|
||||||
delete parsed.query.default;
|
req.url.path_list[1] = def;
|
||||||
delete parsed.search;
|
req.url.pathname = req.url.path_list.join('/');
|
||||||
parsed.path_list[1] = def;
|
var newUrl = req.url.toString();
|
||||||
parsed.pathname = "/" + parsed.path_list.join("/");
|
|
||||||
var newUrl = url.format(parsed);
|
|
||||||
callback({
|
callback({
|
||||||
status: img_status,
|
status: img_status,
|
||||||
redirect: newUrl,
|
redirect: newUrl,
|
||||||
@ -53,9 +51,9 @@ function handle_default(img_status, userId, size, def, req, err, callback) {
|
|||||||
// GET avatar request
|
// GET avatar request
|
||||||
module.exports = function(req, callback) {
|
module.exports = function(req, callback) {
|
||||||
var userId = (req.url.path_list[1] || "").split(".")[0];
|
var userId = (req.url.path_list[1] || "").split(".")[0];
|
||||||
var size = parseInt(req.url.query.size) || config.avatars.default_size;
|
var size = parseInt(req.url.searchParams.get("size")) || config.avatars.default_size;
|
||||||
var def = req.url.query.default;
|
var def = req.url.searchParams.get("default");
|
||||||
var overlay = Object.prototype.hasOwnProperty.call(req.url.query, "overlay") || Object.prototype.hasOwnProperty.call(req.url.query, "helm");
|
var overlay = req.url.searchParams.has("overlay") || req.url.searchParams.has("helm");
|
||||||
|
|
||||||
// check for extra paths
|
// check for extra paths
|
||||||
if (req.url.path_list.length > 2) {
|
if (req.url.path_list.length > 2) {
|
||||||
@ -67,6 +65,9 @@ module.exports = function(req, callback) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// strip dashes
|
||||||
|
userId = userId.replace(/-/g, "");
|
||||||
|
|
||||||
// Prevent app from crashing/freezing
|
// Prevent app from crashing/freezing
|
||||||
if (size < config.avatars.min_size || size > config.avatars.max_size) {
|
if (size < config.avatars.min_size || size > config.avatars.max_size) {
|
||||||
// "Unprocessable Entity", valid request, but semantically erroneous:
|
// "Unprocessable Entity", valid request, but semantically erroneous:
|
||||||
@ -84,9 +85,6 @@ module.exports = function(req, callback) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// strip dashes
|
|
||||||
userId = userId.replace(/-/g, "");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
helpers.get_avatar(req.id, userId, overlay, size, function(err, status, image, hash) {
|
helpers.get_avatar(req.id, userId, overlay, size, function(err, status, image, hash) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ var cache = require("../cache");
|
|||||||
// GET cape request
|
// GET cape request
|
||||||
module.exports = function(req, callback) {
|
module.exports = function(req, callback) {
|
||||||
var userId = (req.url.path_list[1] || "").split(".")[0];
|
var userId = (req.url.path_list[1] || "").split(".")[0];
|
||||||
var def = req.url.query.default;
|
var def = req.url.searchParams.get('default');
|
||||||
var rid = req.id;
|
var rid = req.id;
|
||||||
|
|
||||||
// check for extra paths
|
// check for extra paths
|
||||||
@ -17,6 +17,8 @@ module.exports = function(req, callback) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// strip dashes
|
||||||
|
userId = userId.replace(/-/g, "");
|
||||||
if (!helpers.id_valid(userId)) {
|
if (!helpers.id_valid(userId)) {
|
||||||
callback({
|
callback({
|
||||||
status: -2,
|
status: -2,
|
||||||
@ -25,9 +27,6 @@ module.exports = function(req, callback) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// strip dashes
|
|
||||||
userId = userId.replace(/-/g, "");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
helpers.get_cape(rid, userId, function(err, hash, status, image) {
|
helpers.get_cape(rid, userId, function(err, hash, status, image) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@ -17,12 +17,10 @@ function handle_default(rid, scale, overlay, body, img_status, userId, size, def
|
|||||||
if (defname !== "steve" && defname !== "mhf_steve" && defname !== "alex" && defname !== "mhf_alex") {
|
if (defname !== "steve" && defname !== "mhf_steve" && defname !== "alex" && defname !== "mhf_alex") {
|
||||||
if (helpers.id_valid(def)) {
|
if (helpers.id_valid(def)) {
|
||||||
// clean up the old URL to match new image
|
// clean up the old URL to match new image
|
||||||
var parsed = req.url;
|
req.url.searchParams.delete('default');
|
||||||
delete parsed.query.default;
|
req.url.path_list[2] = def;
|
||||||
delete parsed.search;
|
req.url.pathname = req.url.path_list.join('/');
|
||||||
parsed.path_list[2] = def;
|
var newUrl = req.url.toString();
|
||||||
parsed.pathname = "/" + parsed.path_list.join("/");
|
|
||||||
var newUrl = url.format(parsed);
|
|
||||||
callback({
|
callback({
|
||||||
status: img_status,
|
status: img_status,
|
||||||
redirect: newUrl,
|
redirect: newUrl,
|
||||||
@ -62,9 +60,9 @@ module.exports = function(req, callback) {
|
|||||||
var rid = req.id;
|
var rid = req.id;
|
||||||
var body = raw_type === "body";
|
var body = raw_type === "body";
|
||||||
var userId = (req.url.path_list[2] || "").split(".")[0];
|
var userId = (req.url.path_list[2] || "").split(".")[0];
|
||||||
var def = req.url.query.default;
|
var def = req.url.searchParams.get("default");
|
||||||
var scale = parseInt(req.url.query.scale) || config.renders.default_scale;
|
var scale = parseInt(req.url.searchParams.get("scale")) || config.renders.default_scale;
|
||||||
var overlay = Object.prototype.hasOwnProperty.call(req.url.query, "overlay") || Object.prototype.hasOwnProperty.call(req.url.query, "helm");
|
var overlay = req.url.searchParams.has("overlay") || req.url.searchParams.has("helm");
|
||||||
|
|
||||||
// check for extra paths
|
// check for extra paths
|
||||||
if (req.url.path_list.length > 3) {
|
if (req.url.path_list.length > 3) {
|
||||||
@ -85,6 +83,9 @@ module.exports = function(req, callback) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// strip dashes
|
||||||
|
userId = userId.replace(/-/g, "");
|
||||||
|
|
||||||
if (scale < config.renders.min_scale || scale > config.renders.max_scale) {
|
if (scale < config.renders.min_scale || scale > config.renders.max_scale) {
|
||||||
callback({
|
callback({
|
||||||
status: -2,
|
status: -2,
|
||||||
@ -99,9 +100,6 @@ module.exports = function(req, callback) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// strip dashes
|
|
||||||
userId = userId.replace(/-/g, "");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
helpers.get_render(rid, userId, scale, overlay, body, function(err, status, hash, image) {
|
helpers.get_render(rid, userId, scale, overlay, body, function(err, status, hash, image) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@ -14,12 +14,10 @@ function handle_default(img_status, userId, def, req, err, callback) {
|
|||||||
if (defname !== "steve" && defname !== "mhf_steve" && defname !== "alex" && defname !== "mhf_alex") {
|
if (defname !== "steve" && defname !== "mhf_steve" && defname !== "alex" && defname !== "mhf_alex") {
|
||||||
if (helpers.id_valid(def)) {
|
if (helpers.id_valid(def)) {
|
||||||
// clean up the old URL to match new image
|
// clean up the old URL to match new image
|
||||||
var parsed = req.url;
|
req.url.searchParams.delete('default');
|
||||||
delete parsed.query.default;
|
req.url.path_list[1] = def;
|
||||||
delete parsed.search;
|
req.url.pathname = req.url.path_list.join('/');
|
||||||
parsed.path_list[1] = def;
|
var newUrl = req.url.toString();
|
||||||
parsed.pathname = "/" + parsed.path_list.join("/");
|
|
||||||
var newUrl = url.format(parsed);
|
|
||||||
callback({
|
callback({
|
||||||
status: img_status,
|
status: img_status,
|
||||||
redirect: newUrl,
|
redirect: newUrl,
|
||||||
@ -62,7 +60,7 @@ function handle_default(img_status, userId, def, req, err, callback) {
|
|||||||
// GET skin request
|
// GET skin request
|
||||||
module.exports = function(req, callback) {
|
module.exports = function(req, callback) {
|
||||||
var userId = (req.url.path_list[1] || "").split(".")[0];
|
var userId = (req.url.path_list[1] || "").split(".")[0];
|
||||||
var def = req.url.query.default;
|
var def = req.url.searchParams.get("default");
|
||||||
var rid = req.id;
|
var rid = req.id;
|
||||||
|
|
||||||
// check for extra paths
|
// check for extra paths
|
||||||
@ -75,6 +73,8 @@ module.exports = function(req, callback) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// strip dashes
|
||||||
|
userId = userId.replace(/-/g, "");
|
||||||
if (!helpers.id_valid(userId)) {
|
if (!helpers.id_valid(userId)) {
|
||||||
callback({
|
callback({
|
||||||
status: -2,
|
status: -2,
|
||||||
@ -83,9 +83,6 @@ module.exports = function(req, callback) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// strip dashes
|
|
||||||
userId = userId.replace(/-/g, "");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
helpers.get_skin(rid, userId, function(err, hash, status, image, slim) {
|
helpers.get_skin(rid, userId, function(err, hash, status, image, slim) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
var querystring = require("querystring");
|
var querystring = require("querystring");
|
||||||
var response = require("./response");
|
var response = require("./response");
|
||||||
|
var helpers = require("./helpers.js");
|
||||||
var toobusy = require("toobusy-js");
|
var toobusy = require("toobusy-js");
|
||||||
var logging = require("./logging");
|
var logging = require("./logging");
|
||||||
var config = require("../config");
|
var config = require("../config");
|
||||||
@ -21,7 +22,9 @@ var routes = {
|
|||||||
|
|
||||||
// serves assets from lib/public
|
// serves assets from lib/public
|
||||||
function asset_request(req, callback) {
|
function asset_request(req, callback) {
|
||||||
var filename = path.join(__dirname, "public", req.url.path_list.join("/"));
|
const filename = path.join(__dirname, "public", ...req.url.path_list);
|
||||||
|
const relative = path.relative(path.join(__dirname, "public"), filename);
|
||||||
|
if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) {
|
||||||
fs.access(filename, function(fs_err) {
|
fs.access(filename, function(fs_err) {
|
||||||
if (!fs_err) {
|
if (!fs_err) {
|
||||||
fs.readFile(filename, function(err, data) {
|
fs.readFile(filename, function(err, data) {
|
||||||
@ -39,6 +42,13 @@ function asset_request(req, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
callback({
|
||||||
|
body: "Forbidden",
|
||||||
|
status: -2,
|
||||||
|
code: 403,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generates a 12 character random string
|
// generates a 12 character random string
|
||||||
@ -46,26 +56,18 @@ function request_id() {
|
|||||||
return Math.random().toString(36).substring(2, 14);
|
return Math.random().toString(36).substring(2, 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
// splits a URL path into an Array
|
// splits decoded URL path into an Array
|
||||||
// the path is resolved and decoded
|
|
||||||
function path_list(pathname) {
|
function path_list(pathname) {
|
||||||
// remove double and trailing slashes
|
|
||||||
pathname = pathname.replace(/\/\/+/g, "/").replace(/(.)\/$/, "$1");
|
|
||||||
var list = pathname.split("/");
|
var list = pathname.split("/");
|
||||||
list.shift();
|
list.shift();
|
||||||
for (var i = 0; i < list.length; i++) {
|
|
||||||
// URL decode
|
|
||||||
list[i] = querystring.unescape(list[i]);
|
|
||||||
}
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles the +req+ by routing to the request to the appropriate module
|
// handles the +req+ by routing to the request to the appropriate module
|
||||||
function requestHandler(req, res) {
|
function requestHandler(req, res) {
|
||||||
req.url = url.parse(req.url, true);
|
req.url = new URL(decodeURI(req.url), 'http://' + req.headers.host);
|
||||||
req.url.query = req.url.query || {};
|
req.url.pathname = path.resolve('/', req.url.pathname);
|
||||||
req.url.path_list = path_list(req.url.pathname);
|
req.url.path_list = path_list(req.url.pathname);
|
||||||
|
|
||||||
req.id = request_id();
|
req.id = request_id();
|
||||||
req.start = Date.now();
|
req.start = Date.now();
|
||||||
|
|
||||||
@ -166,6 +168,7 @@ exp.boot = function(callback) {
|
|||||||
|
|
||||||
// Close the server
|
// Close the server
|
||||||
exp.close = function(callback) {
|
exp.close = function(callback) {
|
||||||
|
helpers.stoplog();
|
||||||
server.close(callback);
|
server.close(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -76,7 +76,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<p>You can use <a rel="nofollow" target="_blank" href="https://mcuuid.net">mcuuid.net</a> to find the UUID of a username.</p>
|
<p>You can use <a rel="nofollow" target="_blank" href="https://minecraftuuid.com">minecraftuuid.com</a> to find the UUID of a username.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="avatars">
|
<section id="avatars">
|
||||||
|
|||||||
3248
package-lock.json
generated
3248
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -1,29 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "crafatar",
|
"name": "crafatar",
|
||||||
"version": "2.1.3",
|
"version": "2.1.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node www.js",
|
"start": "node www.js",
|
||||||
"test": "mocha",
|
"test": "mocha"
|
||||||
"test-travis": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "12.16.1"
|
"node": "12.16.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@randy.tarampi/lwip": "^1.1.0",
|
"@randy.tarampi/lwip": "^1.3.1",
|
||||||
"canvas": "^2.6.1",
|
"canvas": "^2.6.1",
|
||||||
"crc": "^3.8.0",
|
"crc": "^3.8.0",
|
||||||
"ejs": "^3.0.1",
|
"ejs": "^3.1.5",
|
||||||
"mime": "^2.4.4",
|
"mime": "^2.4.6",
|
||||||
"redis": "^3.0.2",
|
"redis": "^3.0.2",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"toobusy-js": "^0.5.1"
|
"toobusy-js": "^0.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"coveralls": "^3.0.11",
|
"mocha": "^7.2.0"
|
||||||
"istanbul": "^0.4.5",
|
|
||||||
"mocha": "^7.1.1",
|
|
||||||
"mocha-lcov-reporter": "^1.3.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
63
test/bulk.sh
63
test/bulk.sh
@ -1,36 +1,55 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
hostname="crafatar.com"
|
||||||
async="true"
|
async="true"
|
||||||
|
random="false"
|
||||||
interval="0.1"
|
interval="0.1"
|
||||||
if [ "$1" = "-s" ]; then
|
|
||||||
async=""
|
|
||||||
shift
|
|
||||||
elif [ "$1" = "-i" ]; then
|
|
||||||
interval="$2"
|
|
||||||
shift 2
|
|
||||||
fi
|
|
||||||
host="$1"
|
|
||||||
shift
|
|
||||||
if [ -z "$host" ] || [ ! -z "$@" ]; then
|
|
||||||
echo "Usage: $0 [-s | -i <interval>] <host uri>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# insert newline after uuids
|
usage() {
|
||||||
ids="$(cat 'uuids.txt')"
|
echo "Usage: $0 [-s | -r | -i <interval> | -h <hostname>]... <host uri>" >&2
|
||||||
# `brew install coreutils` on OS X
|
exit 1
|
||||||
ids="$(shuf <<< "$ids" 2>/dev/null || gshuf <<< "$ids")"
|
}
|
||||||
|
|
||||||
|
get_ids() {
|
||||||
|
local shuf
|
||||||
|
if [ "$random" = "true" ]; then
|
||||||
|
while true; do uuid -v 4; done
|
||||||
|
else
|
||||||
|
# `brew install coreutils` on OS X for gshuf
|
||||||
|
shuf=$(command -v shuf gshuf)
|
||||||
|
# randomize ids
|
||||||
|
$shuf < uuids.txt
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
bulk() {
|
bulk() {
|
||||||
trap return INT
|
trap return INT # return from this function on Ctrl+C
|
||||||
echo "$ids" | while read id; do
|
get_ids | while read id; do
|
||||||
if [ -z "$async" ]; then
|
if [ "$async" = "false" ]; then
|
||||||
curl -sSL -o /dev/null -w "%{url_effective} %{http_code} %{time_total}s\\n" -- "$host/avatars/$id?overlay"
|
curl -H "Host: $hostname" -sSL -o /dev/null -w "%{url_effective} %{http_code} %{time_total}s\\n" -- "$host/avatars/$id?overlay"
|
||||||
else
|
else
|
||||||
curl -sSL -o /dev/null -w "%{url_effective} %{http_code} %{time_total}s\\n" -- "$host/avatars/$id?overlay" &
|
curl -H "Host: $hostname" -sSL -o /dev/null -w "%{url_effective} %{http_code} %{time_total}s\\n" -- "$host/avatars/$id?overlay" &
|
||||||
sleep "$interval"
|
sleep "$interval"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while [ $# != 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-s)
|
||||||
|
async="false";;
|
||||||
|
-r)
|
||||||
|
random="true";;
|
||||||
|
-i)
|
||||||
|
interval="$2"
|
||||||
|
shift;;
|
||||||
|
*)
|
||||||
|
[ -n "$host" ] && usage
|
||||||
|
host="$1";;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -z "$host" ] && usage
|
||||||
|
|
||||||
time bulk
|
time bulk
|
||||||
109
test/test.js
109
test/test.js
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
// no spam
|
// no spam
|
||||||
var logging = require("../lib/logging");
|
var logging = require("../lib/logging");
|
||||||
if (process.env.VERBOSE_TEST !== "true" && process.env.TRAVIS !== "true") {
|
if (process.env.VERBOSE_TEST !== "true") {
|
||||||
logging.log = logging.debug = logging.warn = logging.error = function() {};
|
logging.log = logging.debug = logging.warn = logging.error = function() {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,8 +88,8 @@ describe("Crafatar", function() {
|
|||||||
assert.strictEqual(helpers.id_valid("1DCEF164FF0A47F2B9A691385C774EE7"), true);
|
assert.strictEqual(helpers.id_valid("1DCEF164FF0A47F2B9A691385C774EE7"), true);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
it("dashed uuid is valid", function(done) {
|
it("dashed uuid is not valid", function(done) {
|
||||||
assert.strictEqual(helpers.id_valid("0098cb60-fa8e-427c-b299-793cbd302c9a"), true);
|
assert.strictEqual(helpers.id_valid("0098cb60-fa8e-427c-b299-793cbd302c9a"), false);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
it("username is invalid", function(done) {
|
it("username is invalid", function(done) {
|
||||||
@ -158,7 +158,7 @@ describe("Crafatar", function() {
|
|||||||
it("should time out on skin download", function(done) {
|
it("should time out on skin download", function(done) {
|
||||||
var original_timeout = config.http_timeout;
|
var original_timeout = config.http_timeout;
|
||||||
config.server.http_timeout = 1;
|
config.server.http_timeout = 1;
|
||||||
networking.get_from(rid(), "http://textures.minecraft.net/texture/477be35554684c28bdeee4cf11c591d3c88afb77e0b98da893fd7bc318c65184", function(body, res, error) {
|
networking.get_from(rid(), config.endpoints.textures_url + "477be35554684c28bdeee4cf11c591d3c88afb77e0b98da893fd7bc318c65184", function(body, res, error) {
|
||||||
assert.notStrictEqual(["ETIMEDOUT", "ESOCKETTIMEDOUT"].indexOf(error.code), -1);
|
assert.notStrictEqual(["ETIMEDOUT", "ESOCKETTIMEDOUT"].indexOf(error.code), -1);
|
||||||
config.server.http_timeout = original_timeout;
|
config.server.http_timeout = original_timeout;
|
||||||
done();
|
done();
|
||||||
@ -166,7 +166,7 @@ describe("Crafatar", function() {
|
|||||||
});
|
});
|
||||||
it("should not find the skin", function(done) {
|
it("should not find the skin", function(done) {
|
||||||
assert.doesNotThrow(function() {
|
assert.doesNotThrow(function() {
|
||||||
networking.get_from(rid(), "http://textures.minecraft.net/texture/this-does-not-exist", function(img, response, err) {
|
networking.get_from(rid(), config.endpoints.textures_url + "this-does-not-exist", function(img, response, err) {
|
||||||
assert.strictEqual(err, null); // no error here, but it shouldn't throw exceptions
|
assert.strictEqual(err, null); // no error here, but it shouldn't throw exceptions
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -298,20 +298,24 @@ describe("Crafatar", function() {
|
|||||||
var server_tests = {
|
var server_tests = {
|
||||||
"avatar with existing uuid": {
|
"avatar with existing uuid": {
|
||||||
url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
|
url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
|
||||||
crc32: [3337292777],
|
crc32: [4264176600],
|
||||||
|
},
|
||||||
|
"avatar with existing dashed uuid": {
|
||||||
|
url: "http://localhost:3000/avatars/853c80ef-3c37-49fd-aa49938b674adae6?size=16",
|
||||||
|
crc32: [4264176600],
|
||||||
},
|
},
|
||||||
"avatar with non-existent uuid": {
|
"avatar with non-existent uuid": {
|
||||||
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16",
|
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16",
|
||||||
crc32: [2416827277, 1243826040],
|
crc32: [3348154329],
|
||||||
},
|
},
|
||||||
"avatar with non-existent uuid defaulting to mhf_alex": {
|
"avatar with non-existent uuid defaulting to mhf_alex": {
|
||||||
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=mhf_alex",
|
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=mhf_alex",
|
||||||
crc32: [862751081, 809395677],
|
crc32: [73899130],
|
||||||
},
|
},
|
||||||
"avatar with non-existent uuid defaulting to uuid": {
|
"avatar with non-existent uuid defaulting to uuid": {
|
||||||
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
|
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
|
||||||
crc32: [0],
|
crc32: [0],
|
||||||
redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
|
redirect: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
|
||||||
},
|
},
|
||||||
"avatar with non-existent uuid defaulting to url": {
|
"avatar with non-existent uuid defaulting to url": {
|
||||||
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
||||||
@ -320,20 +324,20 @@ describe("Crafatar", function() {
|
|||||||
},
|
},
|
||||||
"overlay avatar with existing uuid": {
|
"overlay avatar with existing uuid": {
|
||||||
url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay",
|
url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay",
|
||||||
crc32: [1710265722],
|
crc32: [575355728],
|
||||||
},
|
},
|
||||||
"overlay avatar with non-existent uuid": {
|
"overlay avatar with non-existent uuid": {
|
||||||
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay",
|
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay",
|
||||||
crc32: [2416827277, 1243826040],
|
crc32: [3348154329],
|
||||||
},
|
},
|
||||||
"overlay avatar with non-existent uuid defaulting to mhf_alex": {
|
"overlay avatar with non-existent uuid defaulting to mhf_alex": {
|
||||||
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=mhf_alex",
|
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=mhf_alex",
|
||||||
crc32: [862751081, 809395677],
|
crc32: [73899130],
|
||||||
},
|
},
|
||||||
"overlay avatar with non-existent uuid defaulting to uuid": {
|
"overlay avatar with non-existent uuid defaulting to uuid": {
|
||||||
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
|
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
|
||||||
crc32: [0],
|
crc32: [0],
|
||||||
redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
|
redirect: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
|
||||||
},
|
},
|
||||||
"overlay avatar with non-existent uuid defaulting to url": {
|
"overlay avatar with non-existent uuid defaulting to url": {
|
||||||
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
||||||
@ -342,7 +346,7 @@ describe("Crafatar", function() {
|
|||||||
},
|
},
|
||||||
"cape with existing uuid": {
|
"cape with existing uuid": {
|
||||||
url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6",
|
url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6",
|
||||||
crc32: [2556702429],
|
crc32: [985789174, 2099310578],
|
||||||
},
|
},
|
||||||
"cape with non-existent uuid": {
|
"cape with non-existent uuid": {
|
||||||
url: "http://localhost:3000/capes/00000000000000000000000000000000",
|
url: "http://localhost:3000/capes/00000000000000000000000000000000",
|
||||||
@ -355,20 +359,20 @@ describe("Crafatar", function() {
|
|||||||
},
|
},
|
||||||
"skin with existing uuid": {
|
"skin with existing uuid": {
|
||||||
url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6",
|
url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6",
|
||||||
crc32: [26500336],
|
crc32: [1759176487],
|
||||||
},
|
},
|
||||||
"skin with non-existent uuid": {
|
"skin with non-existent uuid": {
|
||||||
url: "http://localhost:3000/skins/00000000000000000000000000000000",
|
url: "http://localhost:3000/skins/00000000000000000000000000000000",
|
||||||
crc32: [981937087],
|
crc32: [1853029228],
|
||||||
},
|
},
|
||||||
"skin with non-existent uuid defaulting to mhf_alex": {
|
"skin with non-existent uuid defaulting to mhf_alex": {
|
||||||
url: "http://localhost:3000/skins/00000000000000000000000000000000?default=mhf_alex",
|
url: "http://localhost:3000/skins/00000000000000000000000000000000?default=mhf_alex",
|
||||||
crc32: [2298915739],
|
crc32: [427506205],
|
||||||
},
|
},
|
||||||
"skin with non-existent uuid defaulting to uuid": {
|
"skin with non-existent uuid defaulting to uuid": {
|
||||||
url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
|
url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
|
||||||
crc32: [0],
|
crc32: [0],
|
||||||
redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16",
|
redirect: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6?size=16",
|
||||||
},
|
},
|
||||||
"skin with non-existent uuid defaulting to url": {
|
"skin with non-existent uuid defaulting to url": {
|
||||||
url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
||||||
@ -390,7 +394,7 @@ describe("Crafatar", function() {
|
|||||||
"head render with non-existent uuid defaulting to uuid": {
|
"head render with non-existent uuid defaulting to uuid": {
|
||||||
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
|
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
|
||||||
crc32: [0],
|
crc32: [0],
|
||||||
redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2",
|
redirect: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2",
|
||||||
},
|
},
|
||||||
"head render with non-existent uuid defaulting to url": {
|
"head render with non-existent uuid defaulting to url": {
|
||||||
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
||||||
@ -412,7 +416,7 @@ describe("Crafatar", function() {
|
|||||||
"overlay head with non-existent uuid defaulting to uuid": {
|
"overlay head with non-existent uuid defaulting to uuid": {
|
||||||
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6",
|
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6",
|
||||||
crc32: [0],
|
crc32: [0],
|
||||||
redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay=",
|
redirect: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay=",
|
||||||
},
|
},
|
||||||
"overlay head render with non-existent uuid defaulting to url": {
|
"overlay head render with non-existent uuid defaulting to url": {
|
||||||
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
||||||
@ -421,7 +425,7 @@ describe("Crafatar", function() {
|
|||||||
},
|
},
|
||||||
"body render with existing uuid": {
|
"body render with existing uuid": {
|
||||||
url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2",
|
url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2",
|
||||||
crc32: [2745192436],
|
crc32: [1144887125],
|
||||||
},
|
},
|
||||||
"body render with non-existent uuid": {
|
"body render with non-existent uuid": {
|
||||||
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2",
|
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2",
|
||||||
@ -429,12 +433,12 @@ describe("Crafatar", function() {
|
|||||||
},
|
},
|
||||||
"body render with non-existent uuid defaulting to mhf_alex": {
|
"body render with non-existent uuid defaulting to mhf_alex": {
|
||||||
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=mhf_alex",
|
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=mhf_alex",
|
||||||
crc32: [1255106465],
|
crc32: [4280894468],
|
||||||
},
|
},
|
||||||
"body render with non-existent uuid defaulting to uuid": {
|
"body render with non-existent uuid defaulting to uuid": {
|
||||||
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
|
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
|
||||||
crc32: [0],
|
crc32: [0],
|
||||||
redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2",
|
redirect: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2",
|
||||||
},
|
},
|
||||||
"body render with non-existent uuid defaulting to url": {
|
"body render with non-existent uuid defaulting to url": {
|
||||||
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
||||||
@ -443,7 +447,7 @@ describe("Crafatar", function() {
|
|||||||
},
|
},
|
||||||
"overlay body render with existing uuid": {
|
"overlay body render with existing uuid": {
|
||||||
url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay",
|
url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay",
|
||||||
crc32: [2441671793],
|
crc32: [1107696668],
|
||||||
},
|
},
|
||||||
"overlay body render with non-existent uuid": {
|
"overlay body render with non-existent uuid": {
|
||||||
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay",
|
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay",
|
||||||
@ -451,7 +455,7 @@ describe("Crafatar", function() {
|
|||||||
},
|
},
|
||||||
"overlay body render with non-existent uuid defaulting to mhf_alex": {
|
"overlay body render with non-existent uuid defaulting to mhf_alex": {
|
||||||
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex",
|
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex",
|
||||||
crc32: [1255106465],
|
crc32: [4280894468],
|
||||||
},
|
},
|
||||||
"overlay body render with non-existent uuid defaulting to url": {
|
"overlay body render with non-existent uuid defaulting to url": {
|
||||||
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
|
||||||
@ -535,7 +539,7 @@ describe("Crafatar", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return a 422 (invalid render type)", function(done) {
|
it("should return a 422 (invalid render type)", function(done) {
|
||||||
request.get("http://localhost:3000/renders/invalid/Jake_0", function(error, res, body) {
|
request.get("http://localhost:3000/renders/invalid/2d5aa9cdaeb049189930461fc9b91cc5", function(error, res, body) {
|
||||||
assert.ifError(error);
|
assert.ifError(error);
|
||||||
assert.strictEqual(res.statusCode, 422);
|
assert.strictEqual(res.statusCode, 422);
|
||||||
done();
|
done();
|
||||||
@ -564,6 +568,30 @@ describe("Crafatar", function() {
|
|||||||
});
|
});
|
||||||
}(loc));
|
}(loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it("should return /public resources", function(done) {
|
||||||
|
request.get("http://localhost:3000/javascript/crafatar.js", function(error, res, body) {
|
||||||
|
assert.ifError(error);
|
||||||
|
assert.strictEqual(res.statusCode, 200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not allow path traversal on /public", function(done) {
|
||||||
|
request.get("http://localhost:3000/../server.js", function(error, res, body) {
|
||||||
|
assert.ifError(error);
|
||||||
|
assert.strictEqual(res.statusCode, 404);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not allow encoded path traversal on /public", function(done) {
|
||||||
|
request.get("http://localhost:3000/%2E%2E/server.js", function(error, res, body) {
|
||||||
|
assert.ifError(error);
|
||||||
|
assert.strictEqual(res.statusCode, 404);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// we have to make sure that we test both a 32x64 and 64x64 skin
|
// we have to make sure that we test both a 32x64 and 64x64 skin
|
||||||
@ -584,7 +612,7 @@ describe("Crafatar", function() {
|
|||||||
|
|
||||||
describe("Networking: Cape", function() {
|
describe("Networking: Cape", function() {
|
||||||
it("should not fail (guaranteed cape)", function(done) {
|
it("should not fail (guaranteed cape)", function(done) {
|
||||||
helpers.get_cape(rid(), "Dinnerbone", function(err, hash, status, img) {
|
helpers.get_cape(rid(), "61699b2ed3274a019f1e0ea8c3f06bc6", function(err, hash, status, img) {
|
||||||
assert.strictEqual(err, null);
|
assert.strictEqual(err, null);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -593,13 +621,13 @@ describe("Crafatar", function() {
|
|||||||
before(function() {
|
before(function() {
|
||||||
cache.get_redis().flushall();
|
cache.get_redis().flushall();
|
||||||
});
|
});
|
||||||
helpers.get_cape(rid(), "Dinnerbone", function(err, hash, status, img) {
|
helpers.get_cape(rid(), "61699b2ed3274a019f1e0ea8c3f06bc6", function(err, hash, status, img) {
|
||||||
assert.strictEqual(err, null);
|
assert.strictEqual(err, null);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("should not be found", function(done) {
|
it("should not be found", function(done) {
|
||||||
helpers.get_cape(rid(), "Jake_0", function(err, hash, status, img) {
|
helpers.get_cape(rid(), "2d5aa9cdaeb049189930461fc9b91cc5", function(err, hash, status, img) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.strictEqual(img, null);
|
assert.strictEqual(img, null);
|
||||||
done();
|
done();
|
||||||
@ -609,7 +637,7 @@ describe("Crafatar", function() {
|
|||||||
|
|
||||||
describe("Networking: Skin", function() {
|
describe("Networking: Skin", function() {
|
||||||
it("should not fail", function(done) {
|
it("should not fail", function(done) {
|
||||||
helpers.get_cape(rid(), "Jake_0", function(err, hash, status, img) {
|
helpers.get_cape(rid(), "2d5aa9cdaeb049189930461fc9b91cc5", function(err, hash, status, img) {
|
||||||
assert.strictEqual(err, null);
|
assert.strictEqual(err, null);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -618,7 +646,7 @@ describe("Crafatar", function() {
|
|||||||
before(function() {
|
before(function() {
|
||||||
cache.get_redis().flushall();
|
cache.get_redis().flushall();
|
||||||
});
|
});
|
||||||
helpers.get_cape(rid(), "Jake_0", function(err, hash, status, img) {
|
helpers.get_cape(rid(), "2d5aa9cdaeb049189930461fc9b91cc5", function(err, hash, status, img) {
|
||||||
assert.strictEqual(err, null);
|
assert.strictEqual(err, null);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -686,15 +714,16 @@ describe("Crafatar", function() {
|
|||||||
cache.get_redis().flushall();
|
cache.get_redis().flushall();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uuid SHOULD be rate limited", function(done) {
|
// Mojang has changed its rate limiting, so we no longer expect to hit the rate limit
|
||||||
networking.get_profile(rid(), uuid, function() {
|
// it("uuid SHOULD be rate limited", function(done) {
|
||||||
networking.get_profile(rid(), uuid, function(err, profile) {
|
// networking.get_profile(rid(), uuid, function() {
|
||||||
assert.strictEqual(err.toString(), "HTTP: 429");
|
// networking.get_profile(rid(), uuid, function(err, profile) {
|
||||||
assert.strictEqual(profile, null);
|
// assert.strictEqual(err.toString(), "HTTP: 429");
|
||||||
done();
|
// assert.strictEqual(profile, null);
|
||||||
});
|
// done();
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
it("CloudFront rate limit is handled", function(done) {
|
it("CloudFront rate limit is handled", function(done) {
|
||||||
var original_rate_limit = config.server.sessions_rate_limit;
|
var original_rate_limit = config.server.sessions_rate_limit;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user