Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
node_modules
.env
config.json
src
env.js
nginx.conf
config.jsonc
data/audit.json
admin.json
keys/

# IDE stuff
Expand Down
95 changes: 95 additions & 0 deletions example.config.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{
// What port should the API listen on?
// Default: 8080
"port": 8080,

// Configuration for the database used by the API.
// Right now, only MongoDB is supported. Sorry.
"database": {
// The URL used to connect to the MongoDB instance.
"mongodb_connection_string": "mongodb+srv://YOUR-CONNECTION-STRING-HERE",
// The database in MongoDB that should be used.
"mongodb_database_name": "YOUR-DATABASE-NAME"
},

// All options relating to the image / photo system.
"images": {
// Completely disables image fetching, will return an error status.
// Default: false
"disable_fetch": false,
// Disables the in-memory cache. Useful on memory-constrained systems.
// Default: false
"disable_caching": false,
// Prevents users from uploading images entirely.
// Default: false
"disable_upload": false,
// The maximum size of uploaded images.
// Default: "10mb"
"max_size": "10mb",

// Replace this with your Firebase bucket URL.
"firebase_bucket_url": "YOUR-BUCKET-URL.appspot.com",

// Replace this with your Firebase client config object.
"firebase_client_config": {
// YOUR CLIENT CONFIG
},

// Replace this with your Firebase admin config object.
"firebase_admin_config": {
// YOUR ADMIN CONFIG
},

// The email and password used to sign in with Firebase.
"firebase_email": "YOUR-FIREBASE-DUMMY-ACCOUNT-EMAIL",
"firebase_password": "YOUR-FIREBASE-DUMMY-ACCOUNT-PASSWORD"
},

// Settings for debugging / developing the API itself.
"debug": {
// Logs the level of server load to the audit log very frequently.
// Default: false
"enable_load_logging": false,
// Prints a tabulated display of all live matchmaking instances
// every few minutes.
// Default: false
"trace_instances": false,
// The identifier for this specific CVR server instance, used in the
// Discord webhook on audit logs. Leave blank, and the webhook won't
// be invoked.
"discord_webhook_id": "My CVR Server :)",
// The URL that should be called to send a Discord webhook. Leave blank
// to prevent the webhook from being called.
"discord_webhook_url": "https://discord.com/YOUR-WEBHOOK-URL-HERE",
// The secret that should be used to verify Git webhooks.
"webhook_git_pull_secret": "YOUR-GIT-PULL-SECRET"
},

// Used to configure the Automatic Error Reporting (AER) system.
"error_reporting": {
// An absolute or relative path to a public key for encrypting
// all received error reports.
"public_key_path": "/PATH/TO/YOUR/KEY.pub",
// The format of the public key.
"public_key_format": "openssh-public"
},

// Settings related to in-game moderation.
"moderation": {
// After how many reports should we automatically time out a player?
// Default: 3
"timeout_after_reports": 3
},

// Config related to the authentication process (login, 2FA, etc)
"authentication": {
// The secret used to sign all JWTs.
"token_secret": "YOUR-TOKEN-SIGNING-SECRET-HERE",
// Twilio account SID, used for 2FA.
"twilio_account_sid": "YOUR-TWILIO-ACCOUNT-SID-HERE",
// Twilio auth token, used for 2FA.
"twilio_auth_token": "YOUR-TWILIO-AUTH-TOKEN-HERE",
// Twilio service SID, used for 2FA.
"twilio_service_sid": "YOUR-TWILIO-SERVICE-SID-HERE"
}
}
76 changes: 44 additions & 32 deletions helpers.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,46 @@
require('dotenv').config();
const fs = require('fs');
const config = require('./config.json');

const notificationTemplates = {
invite: "invite",
friendRequest: "friendRequest",
messageRecieved: "messageRecieved"
};

const jsonc = require('jsonc-parser');
let parseErrors = [];
const config = jsonc.parse(
require('node:fs').readFileSync("config.jsonc", "ascii"),
parseErrors,
{
allowEmptyContent: true,
allowTrailingComma: true,
disallowComments: false,
}
);

module.exports = {
PullPlayerData: PullPlayerData,
PushPlayerData: PushPlayerData,
NotifyPlayer: NotifyPlayer,
ArePlayersAnyFriendType: ArePlayersAnyFriendType,
ArePlayersAcquantances: ArePlayersAcquantances,
ArePlayersFriends: ArePlayersFriends,
ArePlayersFavoriteFriends: ArePlayersFavoriteFriends,
RemoveAcquaintance: RemoveAcquaintance,
RemoveFriend: RemoveFriend,
RemoveFavoriteFriend: RemoveFavoriteFriend,
AddFriend: AddFriend,
AddFavoriteFriend: AddFavoriteFriend,
AddAcquaintance: AddAcquaintance,
getUserID: getUserID,
getAccountCount: getAccountCount,
auditLog: auditLog,
MergeArraysWithoutDuplication: MergeArraysWithoutDuplication,
BanPlayer: BanPlayer,
onPlayerReportedCallback: onPlayerReportedCallback,
check: check
PullPlayerData,
PushPlayerData,
NotifyPlayer,
ArePlayersAnyFriendType,
ArePlayersAcquantances,
ArePlayersFriends,
ArePlayersFavoriteFriends,
RemoveAcquaintance,
RemoveFriend,
RemoveFavoriteFriend,
AddFriend,
AddFavoriteFriend,
AddAcquaintance,
getUserID,
getAccountCount,
auditLog,
MergeArraysWithoutDuplication,
BanPlayer,
onPlayerReportedCallback,
check,
config,
};

/**
Expand All @@ -37,7 +49,7 @@ module.exports = {
* @returns {Object} The player's account data.
*/
async function PullPlayerData(id) {
const db = require('./index').mongoClient.db(process.env.MONGOOSE_DATABASE_NAME);
const db = require('./index').mongoClient.db(config.database.mongodb_database_name);
const account = await db.collection('accounts').findOne({_id: {$eq: id, $exists: true}});
return account;
}
Expand All @@ -48,7 +60,7 @@ async function PullPlayerData(id) {
* @param {Object} data The full data of the specified player's account
*/
async function PushPlayerData(id, data) {
const db = require('./index').mongoClient.db(process.env.MONGOOSE_DATABASE_NAME);
const db = require('./index').mongoClient.db(config.database.mongodb_database_name);
await db.collection('accounts').replaceOne({_id: {$eq: id, $exists: true}}, data, {upsert: true});
}

Expand Down Expand Up @@ -259,7 +271,7 @@ async function AddFavoriteFriend(player1, player2, both) {
* @returns {String|null} The ID of the account associated with that username.
*/
async function getUserID(username) {
const db = require('./index').mongoClient.db(process.env.MONGOOSE_DATABASE_NAME);
const db = require('./index').mongoClient.db(config.database.mongodb_database_name);
const all = await db.collection('accounts').find({}).toArray();
username = username.toLowerCase();
for(const item of all) {
Expand All @@ -273,7 +285,7 @@ async function getUserID(username) {
* @returns {Number} The total number of accounts in the database.
*/
async function getAccountCount() {
const db = require('./index').mongoClient.db(process.env.MONGOOSE_DATABASE_NAME);
const db = require('./index').mongoClient.db(config.database.mongodb_database_name);
const count = await db.collection('accounts').countDocuments();
return count - 1;
}
Expand All @@ -297,14 +309,14 @@ function auditLog(message, isRaw) {

console.log(log);

if (!process.env.AUDIT_SERVER_ID || !process.env.AUDIT_WEBHOOK_URI) return;
if (!config.debug.discord_webhook_url || !config.debug.discord_webhook_id) return;
const globalAuditMessage =
isRaw ?
`API audit log from server.\nID: \`${process.env.AUDIT_SERVER_ID}\`\nMessage:\n${message}` :
`API audit log from server.\nID: \`${process.env.AUDIT_SERVER_ID}\`\nMessage:\`${message}\``;
`API audit log from server.\nID: \`${config.debug.discord_webhook_id}\`\nMessage:\n${message}` :
`API audit log from server.\nID: \`${config.debug.discord_webhook_id}\`\nMessage:\`${message}\``;

fetch(
process.env.AUDIT_WEBHOOK_URI,
config.debug.discord_webhook_url,
{
'method': 'POST',
'headers': {
Expand Down Expand Up @@ -354,9 +366,9 @@ async function onPlayerReportedCallback(reportData) {
reportingData.auth.reportedUsers.splice(index);
await PushPlayerData(reportData.reportingUser, reportingData);
}
} else if (reportedData.auth.recievedReports.length >= config.timeout_at_report_count) {
await BanPlayer(reportData.reportedUser, `Automated timeout for recieving ${config.timeout_at_report_count} or more reports. This timeout will not affect your moderation history unless it is found to be 100% justified.`, 6, reportData.reportingUser);
auditLog(`!! MODERATION ACTION !! User ${reportingData.nickname} (@${reportedData.username}) was timed out for 6 hours for recieving ${config.timeout_at_report_count} reports. Please investigate!`);
} else if (reportedData.auth.recievedReports.length >= config.moderation?.timeout_after_reports ?? 3) {
await BanPlayer(reportData.reportedUser, `Automated timeout for recieving ${config.moderation?.timeout_after_reports ?? 3} or more reports. This timeout will not affect your moderation history unless it is found to be 100% justified.`, 6, reportData.reportingUser);
auditLog(`!! MODERATION ACTION !! User "${reportingData.nickname}" (@${reportedData.username}) was timed out for 6 hours for recieving ${config.moderation?.timeout_after_reports ?? 3} reports. Please investigate!`);
}
}

Expand Down
16 changes: 8 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require('dotenv').config();

const express = require('express');
const fileUpload = require('express-fileupload');
const RateLimit = require('express-rate-limit');
const helpers = require('./helpers');
const config = helpers.config;
const firebaseAuth = require('firebase/auth');

const WebSocketV2_MessageTemplate = {
Expand Down Expand Up @@ -31,8 +33,6 @@ app.use(fileUpload({
limit: '50mb'
}));

const config = require('./config.json');

//#region routers

// /api/accounts/*
Expand Down Expand Up @@ -80,7 +80,7 @@ app.get("/", async (req, res) => {
app.get("/api/versioncheck/:platform/:version_id", async (req, res) => {
let {platform, version_id} = req.params;

let conf = await client.db(process.env.MONGOOSE_DATABASE_NAME).collection('global').findOne({_id: {$eq: "VersionCheckConfig", $exists: true}});
let conf = await client.db(config.database.mongodb_database_name).collection('global').findOne({_id: {$eq: "VersionCheckConfig", $exists: true}});
if(conf == null) {
res.status(500).json({
"code": "internal_error",
Expand Down Expand Up @@ -114,11 +114,11 @@ app.get("/api/dingus", async(req, res) => {

//#endregion

const server = app.listen(config.PORT, '0.0.0.0');
const server = app.listen(config.port ?? 8080, '0.0.0.0');

const { MongoClient } = require('mongodb');

const uri = process.env.MONGOOSE_CONNECTION_STRING;
const uri = config.database.mongodb_connection_string;
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
Expand All @@ -133,11 +133,11 @@ client.connect().then(async (client) => {

console.log("MongoDB Connection Established.");

require('firebase/app').initializeApp(require('./env').firebaseConfig);
require('firebase/app').initializeApp(config.images.firebase_client_config);

const auth = firebaseAuth.getAuth();

const firebaseAuthUser = await firebaseAuth.signInWithEmailAndPassword(auth, process.env.FIREBASE_EMAIL, process.env.FIREBASE_API_SECRET);
const firebaseAuthUser = await firebaseAuth.signInWithEmailAndPassword(auth, config.images.firebase_email, config.images.firebase_password);

if (typeof firebaseAuthUser.user.uid == 'undefined') {
helpers.auditLog('Failed to connect to Firebase - fatal');
Expand Down Expand Up @@ -178,7 +178,7 @@ client.connect().then(async (client) => {
exports.MessagingGatewayServerV1 = MessagingGatewayServerV1;
exports.WebSocketServerV2 = WebSocketServerV2;

helpers.auditLog(`Server Init, API is ready at http://127.0.0.1:${config.PORT}/ \n:D`, false);
helpers.auditLog(`Server Init, API is ready at http://127.0.0.1:${config.port ?? 8080}/ \n:D`, false);

process.on('beforeExit', () => {
helpers.auditLog("Server exit.", false);
Expand Down
9 changes: 5 additions & 4 deletions middleware.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const helpers = require('./helpers');
const config = helpers.config;
const jwt = require('jsonwebtoken');

module.exports = {
Expand All @@ -18,7 +19,7 @@ async function authenticateToken(req, res, next) {
//then we need to authenticate that token in this middleware and return a user
try
{
const tokenData = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
const tokenData = jwt.verify(token, config.authentication.token_secret);
req.user = tokenData;

const data = await helpers.PullPlayerData(tokenData.id);
Expand Down Expand Up @@ -56,7 +57,7 @@ async function authenticateDeveloperToken(req, res, next) {
//then we need to authenticate that token in this middleware and return a user
try
{
const tokenData = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
const tokenData = jwt.verify(token, config.authentication.token_secret);
req.user = tokenData;

const data = await helpers.PullPlayerData(tokenData.id);
Expand Down Expand Up @@ -90,7 +91,7 @@ function authenticateTokenAndTag(tag) {
if (typeof token != 'string') return res.sendStatus(401);

try {
const tokenData = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
const tokenData = jwt.verify(token, config.authentication.token_secret);
req.user = tokenData;

const data = await helpers.PullPlayerData(tokenData.id);
Expand Down Expand Up @@ -123,7 +124,7 @@ async function authenticateToken_internal(token) {
//then we need to authenticate that token in this middleware and return a user
try
{
const tokenData = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
const tokenData = jwt.verify(token, config.authentication.token_secret);

const playerData = await helpers.PullPlayerData(tokenData.id);
if(playerData === null) return {success: false, tokenData: tokenData, playerData: null, reason: "player_not_found"};
Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"firebase": "^9.21.0",
"firebase-admin": "^11.8.0",
"fuse.js": "^6.5.3",
"jsonc-parser": "^3.3.1",
"jsonwebtoken": "^9.0.0",
"mongodb": "^5.5.0",
"node-cache": "^5.1.2",
Expand Down
Loading
Loading