Skip to content

Commit 614924b

Browse files
committed
init
0 parents  commit 614924b

19 files changed

+3200
-0
lines changed

.editorconfig

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# http://editorconfig.org
2+
root = true
3+
4+
[*]
5+
charset = utf-8
6+
end_of_line = lf
7+
indent_size = 2
8+
indent_style = space
9+
insert_final_newline = true
10+
trim_trailing_whitespace = true
11+
# editorconfig-tools is unable to ignore longs strings or urls
12+
max_line_length = off
13+
14+
[*.md]
15+
trim_trailing_whitespace = false

.env.example

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
NODE_ENV=development
2+
PORT=3000

.eslintrc.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"extends": [
3+
"airbnb-base"
4+
],
5+
"env": {
6+
"browser": true,
7+
"commonjs": true,
8+
"es6": true,
9+
"node": true
10+
},
11+
"parserOptions": {
12+
"ecmaVersion": 9,
13+
"sourceType": "module"
14+
},
15+
"globals": {
16+
"process": false
17+
}
18+
}

.gitignore

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
6+
# Runtime data
7+
pids
8+
*.pid
9+
*.seed
10+
*.pid.lock
11+
12+
# Directory for instrumented libs generated by jscoverage/JSCover
13+
lib-cov
14+
15+
# Coverage directory used by tools like istanbul
16+
coverage
17+
18+
# nyc test coverage
19+
.nyc_output
20+
21+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
22+
.grunt
23+
24+
# node-waf configuration
25+
.lock-wscript
26+
27+
# Compiled binary addons (http://nodejs.org/api/addons.html)
28+
build/Release
29+
30+
31+
# Dependency directories
32+
.idea/
33+
.env
34+
.env.production
35+
.env.local
36+
.env.development
37+
.env.docker
38+
node_modules
39+
jspm_packages
40+
41+
# Optional npm cache directory
42+
.npm
43+
44+
# Optional eslint cache
45+
.eslintcache
46+
47+
# Optional REPL history
48+
.node_repl_history
49+
50+
# Output of 'npm pack'
51+
*.tgz
52+
53+
# Yarn Integrity file
54+
.yarn-integrity

app.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const path = require('path');
2+
require('dotenv').config({ path: path.join(__dirname, './.env') });
3+
const config = require('config');
4+
const express = require('express');
5+
const http = require('http');
6+
const logger = require('./modules/Logger');
7+
8+
const app = express();
9+
const port = config.get('port');
10+
11+
// Parse the HTTP message body for input parameters.
12+
// Any parameters that are found will be added to `req.body`.
13+
app.use(require('./middlewares/bodyParserJson'));
14+
app.use(require('./middlewares/bodyParserUrlencoded'));
15+
16+
// Load all API routes.
17+
const inspectLinkRoute = require('./routes/inspectLinkRoute');
18+
19+
// Register the API routes.
20+
app.use('/inspect-links', inspectLinkRoute);
21+
22+
// Use a 404 handler for all requests where no Express route was found.
23+
app.use('*', require('./middlewares/notFoundHandler'));
24+
25+
// The global error handler.
26+
app.use(require('./middlewares/errorHandler'));
27+
28+
// Create a new HTTP server instance and attach our Express app as the request listener.
29+
const server = http.createServer(app);
30+
31+
// Start the HTTP server and listen for connections.
32+
server.listen(port, '0.0.0.0', () => {
33+
logger.info(`Listening on port ${port}`);
34+
});
35+
36+
module.exports = { app, server };

config/default.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const path = require('path');
2+
require('dotenv').config({ path: path.join(__dirname, '../.env') });
3+
4+
const { env } = process;
5+
6+
module.exports = {
7+
env: env.NODE_ENV || 'development',
8+
port: env.PORT || 3000,
9+
};

controllers/inspectLinkController.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const Inspector = require('../modules/Inspector');
2+
const ErrorResponse = require('../modules/ErrorResponse');
3+
const logger = require('../modules/Logger');
4+
5+
exports.store = async (req, res) => {
6+
const {
7+
body: { link },
8+
} = req;
9+
10+
let inspection;
11+
12+
try {
13+
inspection = await Inspector.inspect(link, 10000);
14+
15+
logger.info('Inspected link', inspection);
16+
} catch (error) {
17+
logger.warn('Inspection failed', { link });
18+
logger.error(error);
19+
20+
return res.status(500).json(ErrorResponse.inspectionFailed());
21+
}
22+
23+
return res.status(201).json({
24+
success: true,
25+
inspection,
26+
});
27+
};

middlewares/bodyParserJson.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const parser = require('body-parser');
2+
3+
module.exports = parser.json({
4+
limit: '1mb',
5+
});

middlewares/bodyParserUrlencoded.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const parser = require('body-parser');
2+
3+
module.exports = parser.urlencoded({
4+
extended: true,
5+
parameterLimit: 500,
6+
limit: '1mb',
7+
});

middlewares/errorHandler.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const ErrorResponse = require('../modules/ErrorResponse');
2+
const logger = require('../modules/Logger');
3+
4+
// `next` must be defined in the function arguments,
5+
// otherwise, this middleware will not work!
6+
// eslint-disable-next-line no-unused-vars
7+
module.exports = (err, req, res, next) => {
8+
logger.warn('Request error', {
9+
url: req.originalUrl,
10+
method: req.method,
11+
parameters: {
12+
body: req.body,
13+
query: req.query,
14+
params: req.params,
15+
},
16+
client: {
17+
ip: req.ip,
18+
userAgent: req.headers['user-agent'],
19+
},
20+
});
21+
22+
logger.error(err);
23+
24+
return res.status(500).json(ErrorResponse.serverError());
25+
};

middlewares/notFoundHandler.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const ErrorResponse = require('../modules/ErrorResponse');
2+
3+
module.exports = (req, res) => res.status(404).json(ErrorResponse.notFound());

middlewares/validateInput.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const { validationResult } = require('express-validator');
2+
const ErrorResponse = require('../modules/ErrorResponse');
3+
const logger = require('../modules/Logger');
4+
5+
module.exports = (validations) => async (req, res, next) => {
6+
await Promise.all(validations.map((validation) => validation.run(req)));
7+
8+
const errors = validationResult(req);
9+
10+
if (errors.isEmpty()) {
11+
return next();
12+
}
13+
14+
logger.warn('Input validation failed', {
15+
url: req.originalUrl,
16+
method: req.method,
17+
parameters: {
18+
body: req.body,
19+
query: req.query,
20+
params: req.params,
21+
},
22+
client: {
23+
ip: req.ip,
24+
userAgent: req.headers['user-agent'],
25+
},
26+
errors: errors.array(),
27+
});
28+
29+
return res.status(400).json(ErrorResponse.validationError(errors));
30+
};

modules/ErrorResponse.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const _ = require('lodash');
2+
3+
class ErrorResponse {
4+
static inspectionFailed() {
5+
return {
6+
id: 'INSPECTION_FAILED',
7+
message: 'Failed to inspect the given CS:GO inspect link, please try again.',
8+
};
9+
}
10+
11+
static notFound() {
12+
return {
13+
id: 'NOT_FOUND',
14+
message: 'Whoops, this endpoint does not exist.',
15+
};
16+
}
17+
18+
static serverError() {
19+
return {
20+
id: 'SERVER_ERROR',
21+
message: 'An unexpected server error occurred.',
22+
};
23+
}
24+
25+
static validationError(error) {
26+
const firstError = _.first(error.array());
27+
28+
return {
29+
id: 'VALIDATION_ERROR',
30+
message: `Parameter "${firstError.param}" failed validation: "${firstError.msg}"`,
31+
};
32+
}
33+
}
34+
35+
module.exports = ErrorResponse;

modules/Inspector.js

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
const SteamUser = require('steam-user');
2+
const GlobalOffensive = require('globaloffensive');
3+
const SteamTotp = require('steam-totp');
4+
const _ = require('lodash');
5+
const logger = require('./Logger');
6+
7+
// TODO: Add this to a config or to a database and encrypt it.
8+
const accounts = [];
9+
10+
const clients = [];
11+
12+
accounts.forEach((account) => {
13+
const client = new SteamUser();
14+
const csgo = new GlobalOffensive(client);
15+
16+
client.logOn({
17+
accountName: account.username,
18+
password: account.password,
19+
twoFactorCode: SteamTotp.getAuthCode(account.sharedSecret),
20+
});
21+
22+
client.on('loggedOn', () => {
23+
logger.info(`Logged into Steam as ${client.steamID.getSteam3RenderedID()}`);
24+
client.setPersona(SteamUser.EPersonaState.Online);
25+
client.gamesPlayed(730, true);
26+
});
27+
28+
client.on('error', (e) => {
29+
// Some error occurred during logon
30+
logger.error('Client error');
31+
logger.error(e);
32+
});
33+
34+
client.on('webSession', () => {
35+
logger.info('Got web session');
36+
// Do something with these cookies if you wish
37+
});
38+
39+
csgo.on('connectedToGC', () => {
40+
logger.info('Connected to CS:GO game coordinator');
41+
});
42+
43+
csgo.on('inspectItemTimedOut', (assetid) => {
44+
logger.warn(`Inspect timed out for assetid ${assetid}`);
45+
});
46+
47+
clients.push({
48+
client,
49+
csgo,
50+
});
51+
});
52+
53+
class Inspector {
54+
static getClients() {
55+
return clients;
56+
}
57+
58+
static inspect(url, timeoutMs = 5000) {
59+
return new Promise((resolve, reject) => {
60+
const client = _.sample(_.filter(clients, ((x) => x.csgo.haveGCSession)));
61+
62+
if (_.isUndefined(client)) {
63+
reject(
64+
new Error('There is currently no client available with an active game coordinator connection'),
65+
);
66+
}
67+
68+
client.csgo.inspectItem(url, (item) => {
69+
resolve(item);
70+
});
71+
72+
setTimeout(() => {
73+
reject(new Error(`Inspection timed out after ${timeoutMs} ms`));
74+
}, timeoutMs);
75+
});
76+
}
77+
}
78+
79+
module.exports = Inspector;

0 commit comments

Comments
 (0)