mirror of
https://github.com/Zandercraft/TechnicFlux.git
synced 2026-10-31 04:59:20 +00:00
Move different schemas and their related functions into their own modules.
This commit is contained in:
20
.env.example
20
.env.example
@@ -11,17 +11,17 @@
|
|||||||
# See https://www.mongodb.com/docs/manual/reference/connection-string/
|
# See https://www.mongodb.com/docs/manual/reference/connection-string/
|
||||||
MONGODB_CONN_STRING="mongodb://localhost:27017/technicflux"
|
MONGODB_CONN_STRING="mongodb://localhost:27017/technicflux"
|
||||||
|
|
||||||
# (REQUIRED) Admin Username and Password
|
|
||||||
# The username and password for the administrator account. CHANGE THIS!
|
|
||||||
ADMIN_USER="flux"
|
|
||||||
ADMIN_PASS="flux"
|
|
||||||
|
|
||||||
# (REQUIRED) The name of your TechnicFlux instance.
|
# (REQUIRED) The name of your TechnicFlux instance.
|
||||||
NAME="A TechnicFlux Instance"
|
NAME="A TechnicFlux Instance"
|
||||||
|
|
||||||
# (REQUIRED) The public hostname of the website (i.e. the full domain name without any path, http://, or https://)
|
# (REQUIRED) The public hostname of the website (i.e. the full domain name without any path, http://, or https://)
|
||||||
HOST="example.com"
|
HOST="example.com"
|
||||||
|
|
||||||
|
# (REQUIRED) Admin Username and Password
|
||||||
|
# The username and password for the administrator account. CHANGE THIS!
|
||||||
|
ADMIN_USER="flux"
|
||||||
|
ADMIN_PASS="flux"
|
||||||
|
|
||||||
# (REQUIRED) Solder API Key
|
# (REQUIRED) Solder API Key
|
||||||
# 1. Go to https://www.technicpack.net/
|
# 1. Go to https://www.technicpack.net/
|
||||||
# 2. Sign into your account and navigate to Edit My Profile
|
# 2. Sign into your account and navigate to Edit My Profile
|
||||||
@@ -34,12 +34,18 @@ API_KEY=""
|
|||||||
# Unset behaviour: You will need to specify the author every time.
|
# Unset behaviour: You will need to specify the author every time.
|
||||||
AUTHOR_NAME="John Doe"
|
AUTHOR_NAME="John Doe"
|
||||||
|
|
||||||
|
# --- Rate Limiting ---
|
||||||
|
# With the default values, it would take approximately an average of 3.9462372476248917e+28 minutes*
|
||||||
|
# to brute-force a single API key. If you want to increase the limit for other reasons (performance, etc.)
|
||||||
|
# or are encountering issues and need to increase it, feel free to do so.
|
||||||
|
# * This value may not be accurate. As per our license, I place no warranty or guaruntee on it.
|
||||||
|
|
||||||
# (Optional) API Rate-limit Requests
|
# (Optional) API Rate-limit Requests
|
||||||
# The number of requests that can be made to the API by one IP address within the set window before it will block them.
|
# The number of requests that can be made to the API by one IP address within the set window before it will block them.
|
||||||
# Default: 1000
|
# Unset behaviour: Defaults to 1000
|
||||||
API_RATE_REQUESTS=1000
|
API_RATE_REQUESTS=1000
|
||||||
|
|
||||||
# (Optional) API Rate-limit Window
|
# (Optional) API Rate-limit Window
|
||||||
# The amount of time (in milliseconds) that the request limit is applied to.
|
# The amount of time (in milliseconds) that the request limit is applied to.
|
||||||
# Default: 60000
|
# Unset behaviour: Defaults to 60000
|
||||||
API_RATE_WINDOW=60000
|
API_RATE_WINDOW=60000
|
||||||
|
|||||||
2
bin/www
2
bin/www
@@ -34,7 +34,7 @@ for (let variable of required_variables) {
|
|||||||
const app = require('../app').app;
|
const app = require('../app').app;
|
||||||
const debug = require('debug')('technicflux:server');
|
const debug = require('debug')('technicflux:server');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
const database = require('../database')
|
const database = require('../database/database')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get port from environment and store in Express.
|
* Get port from environment and store in Express.
|
||||||
|
|||||||
523
database.js
523
database.js
@@ -1,523 +0,0 @@
|
|||||||
const debug = require('debug')('technicflux:server')
|
|
||||||
const mongoose = require('mongoose')
|
|
||||||
const { SchemaTypes } = require('mongoose')
|
|
||||||
const bcrypt = require('bcryptjs')
|
|
||||||
const Schema = mongoose.Schema
|
|
||||||
|
|
||||||
// --- Schemas ---
|
|
||||||
const userSchema = new Schema({
|
|
||||||
username: {
|
|
||||||
type: String,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
display_name: String,
|
|
||||||
password: String,
|
|
||||||
created_on: Date,
|
|
||||||
login_history: [{ timestamp: Date, userAgent: String }]
|
|
||||||
})
|
|
||||||
|
|
||||||
const apiKeySchema = new Schema({
|
|
||||||
owner: { type: SchemaTypes.ObjectId, ref: 'technicflux_users' },
|
|
||||||
key: String,
|
|
||||||
name: String,
|
|
||||||
created_at: Date
|
|
||||||
})
|
|
||||||
|
|
||||||
const modpackSchema = new Schema({
|
|
||||||
name: String,
|
|
||||||
display_name: String,
|
|
||||||
url: String,
|
|
||||||
recommended: String,
|
|
||||||
latest: String,
|
|
||||||
builds: [{ type: SchemaTypes.ObjectId, ref: 'technicflux_builds' }],
|
|
||||||
owners: [{ type: SchemaTypes.ObjectId, ref: 'technicflux_users' }],
|
|
||||||
contributors: [{ type: SchemaTypes.ObjectId, ref: 'technicflux_users' }]
|
|
||||||
})
|
|
||||||
|
|
||||||
const modSchema = new Schema({
|
|
||||||
mod_id: Number,
|
|
||||||
name: String,
|
|
||||||
pretty_name: String,
|
|
||||||
version: String,
|
|
||||||
mc_version: String,
|
|
||||||
type: String, // Can be forgemod, neoforgemod, fabricmod, quiltmod, <modloadernamemod>, or modloader(handled separately)
|
|
||||||
md5: String,
|
|
||||||
author: String,
|
|
||||||
description: String,
|
|
||||||
link: String,
|
|
||||||
donate: String,
|
|
||||||
url: String,
|
|
||||||
filesize: Number
|
|
||||||
})
|
|
||||||
|
|
||||||
const buildSchema = new Schema({
|
|
||||||
modpack: { type: SchemaTypes.ObjectId, ref: 'technicflux_modpacks' },
|
|
||||||
version: String,
|
|
||||||
minecraft: String,
|
|
||||||
java: String,
|
|
||||||
memory: Number,
|
|
||||||
forge: String,
|
|
||||||
mods: [{ type: SchemaTypes.ObjectId, ref: 'technicflux_mods' }]
|
|
||||||
})
|
|
||||||
|
|
||||||
// --- Models ---
|
|
||||||
const User = mongoose.model('technicflux_users', userSchema)
|
|
||||||
const APIKey = mongoose.model('technicflux_keys', apiKeySchema)
|
|
||||||
const Modpack = mongoose.model('technicflux_modpacks', modpackSchema)
|
|
||||||
const Mod = mongoose.model('technicflux_mods', modSchema)
|
|
||||||
const Build = mongoose.model('technicflux_builds', buildSchema)
|
|
||||||
|
|
||||||
// --- Functions ---
|
|
||||||
|
|
||||||
exports.connectToDB = (connectionString) => {
|
|
||||||
debug('Connecting to MongoDB database...')
|
|
||||||
mongoose.connect(connectionString)
|
|
||||||
debug('Connected to database.')
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- User-Related Functions ---
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Creates a new user with the given information.
|
|
||||||
*/
|
|
||||||
exports.createUser = (uUsername, uDisplayName, uPassword) => {
|
|
||||||
// Encrypt the password using bcrypt and save user
|
|
||||||
return bcrypt.genSalt(10).then((Salt) => {
|
|
||||||
return bcrypt.hash(uPassword, Salt).then((hash) => {
|
|
||||||
// Create a user object
|
|
||||||
const newUser = new User({
|
|
||||||
username: uUsername,
|
|
||||||
display_name: uDisplayName,
|
|
||||||
password: hash
|
|
||||||
})
|
|
||||||
|
|
||||||
// Commit it to the database
|
|
||||||
return newUser.save().then((user) => {
|
|
||||||
process.stdout.write(`Successfully added new user ${user.username}\n`)
|
|
||||||
return user
|
|
||||||
}).catch((reason) => {
|
|
||||||
process.stdout.write(`ERROR (when saving new user): ${reason}\n`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}).catch((err) => {
|
|
||||||
// Issue creating hashed password
|
|
||||||
process.stdout.write(`ERROR (while hashing password): ${err}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}).catch((err) => {
|
|
||||||
// Issue creating hashed object salt
|
|
||||||
process.stdout.write(`ERROR (while generating password salt): ${err}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Gets the user with the provided id.
|
|
||||||
*/
|
|
||||||
exports.getUserById = (uId) => {
|
|
||||||
// Fetch information about the user with the given id
|
|
||||||
return User.findOne({ _id: uId }).exec().then((user) => {
|
|
||||||
// User found.
|
|
||||||
return user
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No user found with this id.
|
|
||||||
console.log(reason)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Gets the user with the provided username.
|
|
||||||
*/
|
|
||||||
exports.getUserByUsername = (uUsername) => {
|
|
||||||
// Fetch information about the user with the given username
|
|
||||||
return User.findOne({ username: uUsername }).exec().then((user) => {
|
|
||||||
// User found.
|
|
||||||
return user
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No user found with this username.
|
|
||||||
console.log(reason)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Gets an array of all existing users.
|
|
||||||
*/
|
|
||||||
exports.getAllUsers = () => {
|
|
||||||
// Fetch an array of all users
|
|
||||||
return User.find({ }).exec().then((users) => {
|
|
||||||
// Users found.
|
|
||||||
return users
|
|
||||||
}).catch((reason) => {
|
|
||||||
console.log(reason)
|
|
||||||
// Error retrieving users.
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Checks if the corresponding password matches that of the provided username.
|
|
||||||
*/
|
|
||||||
exports.authUser = (uUsername, uPassword, logHistory = false, uAgent = 'Unknown') => {
|
|
||||||
// Fetch information about the user with the given username
|
|
||||||
return User.findOne({ username: uUsername }).exec().then((user) => {
|
|
||||||
// User found. Check password.
|
|
||||||
return bcrypt.compare(uPassword, user.password).then(async (isMatch) => {
|
|
||||||
// Check if it is a match
|
|
||||||
if (isMatch) {
|
|
||||||
// Update the user's login history.
|
|
||||||
if (logHistory) {
|
|
||||||
const newLogin = { timestamp: new Date(), userAgent: String(uAgent) }
|
|
||||||
|
|
||||||
// Add the new login to the user's login history
|
|
||||||
return await User.updateOne(
|
|
||||||
{ username: uUsername },
|
|
||||||
{ $push: { login_history: newLogin } }
|
|
||||||
).exec().then(() => {
|
|
||||||
// Auth was successful and login history updated.
|
|
||||||
user.login_history.push(newLogin)
|
|
||||||
return [true, user]
|
|
||||||
}).catch((reason) => {
|
|
||||||
user.login_history.push(newLogin)
|
|
||||||
// Auth was successful, but failed to update the user's login history.
|
|
||||||
process.stdout.write(`ERROR (while updating user's login history): ${reason}\n`)
|
|
||||||
return [true, user]
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// Auth was successful. Return user without logging to user's history.
|
|
||||||
return [true, user]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// User's authentication was invalid.
|
|
||||||
return [false, undefined]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).catch((reason) => {
|
|
||||||
console.log(reason)
|
|
||||||
// No user found with this username.
|
|
||||||
return [false]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Updates the user with the given username.
|
|
||||||
*/
|
|
||||||
exports.updateUser = (uUsername, object) => {
|
|
||||||
return bcrypt.genSalt(10).then((Salt) => {
|
|
||||||
return bcrypt.hash(object.password, Salt).then((hash) => {
|
|
||||||
if (object.password.length > 0) {
|
|
||||||
object.password = hash
|
|
||||||
} else {
|
|
||||||
object.password = object.old_password
|
|
||||||
}
|
|
||||||
delete object.old_password
|
|
||||||
|
|
||||||
// Update User
|
|
||||||
return User.updateOne(
|
|
||||||
{ username: uUsername },
|
|
||||||
{ $set: object }
|
|
||||||
).exec().then(() => {
|
|
||||||
// Updated user successfully
|
|
||||||
return true
|
|
||||||
}).catch((reason) => {
|
|
||||||
// Failed to update the user
|
|
||||||
process.stdout.write(`ERROR (while updating user): ${reason}\n`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}).catch((err) => {
|
|
||||||
// Issue creating hashed password
|
|
||||||
process.stdout.write(`ERROR (while hashing password): ${err}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}).catch((err) => {
|
|
||||||
// Issue creating hashed object salt
|
|
||||||
process.stdout.write(`ERROR (while generating password salt): ${err}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- API-Key-Related Functions
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Creates a new api key with the given information.
|
|
||||||
*/
|
|
||||||
exports.createAPIKey = (kOwner, kKey, kName) => {
|
|
||||||
// Encrypt the password using bcrypt and save user
|
|
||||||
return bcrypt.genSalt(10).then((Salt) => {
|
|
||||||
return bcrypt.hash(kKey, Salt).then((hash) => {
|
|
||||||
// Create an APIKey object
|
|
||||||
const newKey = new APIKey({
|
|
||||||
owner: kOwner,
|
|
||||||
key: hash,
|
|
||||||
name: kName,
|
|
||||||
created_at: new Date()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Commit it to the database
|
|
||||||
return newKey.save().then((key) => {
|
|
||||||
process.stdout.write(`Successfully added new API key '${kName}' for ${kOwner}\n`)
|
|
||||||
return key
|
|
||||||
}).catch((reason) => {
|
|
||||||
process.stdout.write(`ERROR (when saving new API key): ${reason}\n`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}).catch((err) => {
|
|
||||||
// Issue creating hashed api key
|
|
||||||
process.stdout.write(`ERROR (while hashing key): ${err}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}).catch((err) => {
|
|
||||||
// Issue creating hashed object salt
|
|
||||||
process.stdout.write(`ERROR (while generating api key salt): ${err}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Checks if the corresponding password matches that of the provided username.
|
|
||||||
*/
|
|
||||||
exports.authAPIKey = (uKey) => {
|
|
||||||
// Fetch information about the user with the given username
|
|
||||||
return APIKey.find({ }).exec().then(async (keys) => {
|
|
||||||
// Check if any of the keys match.
|
|
||||||
let valid = [false, undefined]
|
|
||||||
for (const key of keys) {
|
|
||||||
await bcrypt.compare(uKey, key.key).then((isMatch) => {
|
|
||||||
// Check if it is a match
|
|
||||||
if (isMatch) {
|
|
||||||
// The key matched.
|
|
||||||
valid = [true, key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return valid
|
|
||||||
}).catch((reason) => {
|
|
||||||
console.log(reason)
|
|
||||||
// DB error
|
|
||||||
debug('ERROR (DB): Could not fetch API keys.')
|
|
||||||
return [false, undefined]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getAPIKeysByOwner = (mOwner) => {
|
|
||||||
return APIKey.find({ owner: mOwner }).populate('owner').exec().then((keys) => {
|
|
||||||
// API Keys found.
|
|
||||||
return keys
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No API keys found for this owner.
|
|
||||||
debug(`ERROR (DB): Failed to find any API keys with this owner because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.deleteAPIKey = (kId) => {
|
|
||||||
return APIKey.deleteOne({ _id: kId }).exec().then(() => {
|
|
||||||
// Successfully deleted this api key
|
|
||||||
return true
|
|
||||||
}).catch((reason) => {
|
|
||||||
// Failed to delete the api key
|
|
||||||
debug(`ERROR (DB): Failed to delete API Key '${kId}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Modpack-Related Functions ---
|
|
||||||
exports.createModpack = (mSlug, mDisplayName, mOwner) => {
|
|
||||||
// Create a modpack object
|
|
||||||
const newModpack = new Modpack({
|
|
||||||
name: mSlug,
|
|
||||||
display_name: mDisplayName,
|
|
||||||
owners: [mOwner]
|
|
||||||
})
|
|
||||||
|
|
||||||
// Commit it to the database
|
|
||||||
return newModpack.save().then((modpack) => {
|
|
||||||
// Modpack successfully created
|
|
||||||
debug(`Successfully added new project ${modpack.display_name}: ${modpack.name}\n`)
|
|
||||||
return modpack
|
|
||||||
}).catch((reason) => {
|
|
||||||
// Modpack failed to create
|
|
||||||
debug(`ERROR (DB: when saving new modpack): ${reason}\n`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getModpackBySlug = (mSlug) => {
|
|
||||||
return Modpack.findOne({ name: mSlug }).populate('owners').populate('contributors').populate('builds').exec().then((modpack) => {
|
|
||||||
// Modpack found
|
|
||||||
return modpack
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No modpack found with this slug
|
|
||||||
debug(`ERROR (DB): Failed to find a modpack with this slug because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getModpacksByOwner = (mOwner) => {
|
|
||||||
return Modpack.find({ owners: mOwner }).populate('owners').populate('contributors').populate('builds').exec().then((modpacks) => {
|
|
||||||
// Modpacks found.
|
|
||||||
return modpacks
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No modpacks found for this owner.
|
|
||||||
debug(`ERROR (DB): Failed to find any modpacks with this owner because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getModpacksByContributor = (mContributor) => {
|
|
||||||
return Modpack.find({ contributors: mContributor }).populate('owners').populate('contributors').populate('builds').exec().then((modpacks) => {
|
|
||||||
// Modpacks found.
|
|
||||||
return modpacks
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No modpacks found for this collaborator.
|
|
||||||
debug(`ERROR (DB): Failed to find any modpacks with this contributor because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getAllModpacks = () => {
|
|
||||||
// Fetch an array of all modpacks
|
|
||||||
return Modpack.find({}).populate('owners').populate('contributors').populate('builds').exec().then((modpacks) => {
|
|
||||||
// Modpacks found
|
|
||||||
return modpacks
|
|
||||||
}).catch((reason) => {
|
|
||||||
// Failed to get any modpacks.
|
|
||||||
debug(`ERROR (DB): Failed to fetch any modpacks because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.updateModpack = (mSlug, mObject) => {
|
|
||||||
return Modpack.updateOne(
|
|
||||||
{ name: mSlug },
|
|
||||||
{ $set: mObject }
|
|
||||||
).exec().then(() => {
|
|
||||||
// Successfully updated the modpack
|
|
||||||
return true
|
|
||||||
}).catch((reason) => {
|
|
||||||
// Failed to update the modpack
|
|
||||||
debug(`ERROR (DB): Failed to update modpack '${mSlug}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.deleteModpack = (mSlug) => {
|
|
||||||
return Modpack.deleteOne({ name: mSlug }).exec().then(() => {
|
|
||||||
// Successfully deleted this modpack
|
|
||||||
return true
|
|
||||||
}).catch((reason) => {
|
|
||||||
// Failed to delete the modpack
|
|
||||||
debug(`ERROR (DB): Failed to delete modpack '${mSlug}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Mod-Related Functions ---
|
|
||||||
exports.getModBySlug = (mSlug) => {
|
|
||||||
return Mod.find({ name: mSlug }).exec().then((mods) => {
|
|
||||||
if (mods === null || mods.length === 0) {
|
|
||||||
// Mod not found
|
|
||||||
return null
|
|
||||||
} else {
|
|
||||||
// Mod found
|
|
||||||
return {
|
|
||||||
id: mods[0].mod_id,
|
|
||||||
name: mods[0].name,
|
|
||||||
pretty_name: mods[0].pretty_name,
|
|
||||||
author: mods[0].author,
|
|
||||||
description: mods[0].description,
|
|
||||||
link: mods[0].link,
|
|
||||||
versions: mods.map((modVersion) => modVersion.version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No mod found with this slug
|
|
||||||
debug(`ERROR (DB): Failed to find a mod with slug '${mSlug}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getModVersion = (mSlug, mVersion) => {
|
|
||||||
return Mod.findOne({ name: mSlug, version: mVersion }).exec().then((mod) => {
|
|
||||||
// Mod found
|
|
||||||
return mod
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No mod found with this slug and version
|
|
||||||
debug(`ERROR (DB): Failed to find a mod '${mSlug}:${mVersion}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getModsByType = (mType) => {
|
|
||||||
return Mod.find({ type: mType }).exec().then((mods) => {
|
|
||||||
// Mods found
|
|
||||||
return mods
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No mods found with this modloader
|
|
||||||
debug(`ERROR (DB): Failed to find a mod with modloader '${mType}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getModsByMinecraftVersion = (mVersion) => {
|
|
||||||
return Mod.find({ mc_version: mVersion }).exec().then((mods) => {
|
|
||||||
// Mods found
|
|
||||||
return mods
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No mods found with this Minecraft version.
|
|
||||||
debug(`ERROR (DB): Failed to find a mod with Minecraft version '${mVersion}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getModsByMinecraftAndLoaderVersion = (mVersion, mType) => {
|
|
||||||
return Mod.find({ mc_version: mVersion, type: mType }).exec().then((mods) => {
|
|
||||||
// Mods found
|
|
||||||
return mods
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No mods found with this Minecraft and modloader version.
|
|
||||||
debug(`ERROR (DB): Failed to find a mod with Minecraft version '${mVersion}' and modloader version '${mType}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getAllMods = () => {
|
|
||||||
return Mod.find({}).exec().then((mods) => {
|
|
||||||
// Mods found
|
|
||||||
return mods
|
|
||||||
}).catch((reason) => {
|
|
||||||
// No mods found
|
|
||||||
debug(`ERROR (DB): Failed to find any mods because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.updateMod = (mSlug, mObject) => {
|
|
||||||
return Mod.updateOne(
|
|
||||||
{ name: mSlug },
|
|
||||||
{ $set: mObject }
|
|
||||||
).exec().then(() => {
|
|
||||||
// Successfully updated the modpack
|
|
||||||
return true
|
|
||||||
}).catch((reason) => {
|
|
||||||
// Failed to update the modpack
|
|
||||||
debug(`ERROR (DB): Failed to update modpack '${mSlug}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.deleteModpack = (mSlug) => {
|
|
||||||
return Modpack.deleteOne({ name: mSlug }).exec().then(() => {
|
|
||||||
// Successfully deleted this modpack
|
|
||||||
return true
|
|
||||||
}).catch((reason) => {
|
|
||||||
// Failed to delete the modpack
|
|
||||||
debug(`ERROR (DB): Failed to delete modpack '${mSlug}' because of: ${reason}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Modloader-Related Functions ---
|
|
||||||
|
|
||||||
// --- Build-Related Functions ---
|
|
||||||
101
database/api_key.js
Normal file
101
database/api_key.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
const debug = require('debug')('technicflux:server')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
const bcrypt = require('bcryptjs')
|
||||||
|
const { SchemaTypes } = require('mongoose')
|
||||||
|
const Schema = mongoose.Schema
|
||||||
|
|
||||||
|
// --- Schema ---
|
||||||
|
const apiKeySchema = new Schema({
|
||||||
|
owner: { type: SchemaTypes.ObjectId, ref: 'technicflux_users' },
|
||||||
|
key: String,
|
||||||
|
name: String,
|
||||||
|
created_at: Date
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- Model ---
|
||||||
|
exports.APIKey = mongoose.model('technicflux_keys', apiKeySchema)
|
||||||
|
|
||||||
|
// --- Related Functions ---
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a new api key with the given information.
|
||||||
|
*/
|
||||||
|
exports.createAPIKey = (kOwner, kKey, kName) => {
|
||||||
|
// Encrypt the password using bcrypt and save user
|
||||||
|
return bcrypt.genSalt(10).then((Salt) => {
|
||||||
|
return bcrypt.hash(kKey, Salt).then((hash) => {
|
||||||
|
// Create an APIKey object
|
||||||
|
const newKey = new exports.APIKey({
|
||||||
|
owner: kOwner,
|
||||||
|
key: hash,
|
||||||
|
name: kName,
|
||||||
|
created_at: new Date()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Commit it to the database
|
||||||
|
return newKey.save().then((key) => {
|
||||||
|
process.stdout.write(`Successfully added new API key '${kName}' for ${kOwner}\n`)
|
||||||
|
return key
|
||||||
|
}).catch((reason) => {
|
||||||
|
process.stdout.write(`ERROR (when saving new API key): ${reason}\n`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}).catch((err) => {
|
||||||
|
// Issue creating hashed api key
|
||||||
|
process.stdout.write(`ERROR (while hashing key): ${err}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}).catch((err) => {
|
||||||
|
// Issue creating hashed object salt
|
||||||
|
process.stdout.write(`ERROR (while generating api key salt): ${err}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks if the corresponding password matches that of the provided username.
|
||||||
|
*/
|
||||||
|
exports.authAPIKey = (uKey) => {
|
||||||
|
// Fetch information about the user with the given username
|
||||||
|
return exports.APIKey.find({ }).exec().then(async (keys) => {
|
||||||
|
// Check if any of the keys match.
|
||||||
|
let valid = [false, undefined]
|
||||||
|
for (const key of keys) {
|
||||||
|
await bcrypt.compare(uKey, key.key).then((isMatch) => {
|
||||||
|
// Check if it is a match
|
||||||
|
if (isMatch) {
|
||||||
|
// The key matched.
|
||||||
|
valid = [true, key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return valid
|
||||||
|
}).catch((reason) => {
|
||||||
|
console.log(reason)
|
||||||
|
// DB error
|
||||||
|
debug('ERROR (DB): Could not fetch API keys.')
|
||||||
|
return [false, undefined]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getAPIKeysByOwner = (mOwner) => {
|
||||||
|
return exports.APIKey.find({ owner: mOwner }).populate('owner').exec().then((keys) => {
|
||||||
|
// API Keys found.
|
||||||
|
return keys
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No API keys found for this owner.
|
||||||
|
debug(`ERROR (DB): Failed to find any API keys with this owner because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.deleteAPIKey = (kId) => {
|
||||||
|
return exports.APIKey.deleteOne({ _id: kId }).exec().then(() => {
|
||||||
|
// Successfully deleted this api key
|
||||||
|
return true
|
||||||
|
}).catch((reason) => {
|
||||||
|
// Failed to delete the api key
|
||||||
|
debug(`ERROR (DB): Failed to delete API Key '${kId}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
20
database/build.js
Normal file
20
database/build.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const debug = require('debug')('technicflux:server')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
const { SchemaTypes } = require('mongoose')
|
||||||
|
const Schema = mongoose.Schema
|
||||||
|
|
||||||
|
// --- Schema ---
|
||||||
|
const buildSchema = new Schema({
|
||||||
|
modpack: { type: SchemaTypes.ObjectId, ref: 'technicflux_modpacks' },
|
||||||
|
version: String,
|
||||||
|
minecraft: String,
|
||||||
|
java: String,
|
||||||
|
memory: Number,
|
||||||
|
forge: String,
|
||||||
|
mods: [{ type: SchemaTypes.ObjectId, ref: 'technicflux_mods' }]
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- Model ---
|
||||||
|
exports.Build = mongoose.model('technicflux_builds', buildSchema)
|
||||||
|
|
||||||
|
// --- Related Functions ---
|
||||||
11
database/counter.js
Normal file
11
database/counter.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const mongoose = require('mongoose')
|
||||||
|
const Schema = mongoose.Schema
|
||||||
|
|
||||||
|
// --- Incremental Counter ---
|
||||||
|
// Used for tracking the value of fields
|
||||||
|
const CounterSchema = Schema({
|
||||||
|
_id: {type: String, required: true},
|
||||||
|
seq: { type: Number, default: 0 }
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.Counter = mongoose.model('technicflux_modcounter', CounterSchema);
|
||||||
18
database/database.js
Normal file
18
database/database.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const debug = require('debug')('technicflux:server')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
|
// --- Functions ---
|
||||||
|
|
||||||
|
exports.connectToDB = (connectionString) => {
|
||||||
|
debug('Connecting to MongoDB database...')
|
||||||
|
mongoose.connect(connectionString)
|
||||||
|
debug('Connected to database.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema Modules
|
||||||
|
exports.counter = require('./counter')
|
||||||
|
exports.user = require('./user')
|
||||||
|
exports.apiKey = require('./api_key')
|
||||||
|
exports.modpack = require('./modpack')
|
||||||
|
exports.mod = require('./mod')
|
||||||
|
exports.build = require('./build')
|
||||||
145
database/mod.js
Normal file
145
database/mod.js
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
const debug = require('debug')('technicflux:server')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
const database = require('./database')
|
||||||
|
const Schema = mongoose.Schema
|
||||||
|
|
||||||
|
// --- Schema ---
|
||||||
|
const modSchema = new Schema({
|
||||||
|
mod_id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
name: String,
|
||||||
|
pretty_name: String,
|
||||||
|
version: String,
|
||||||
|
mc_version: String,
|
||||||
|
type: String, // Can be forgemod, neoforgemod, fabricmod, quiltmod, <modloadernamemod>, or modloader(handled separately)
|
||||||
|
md5: String,
|
||||||
|
author: String,
|
||||||
|
description: String,
|
||||||
|
link: String,
|
||||||
|
donate: String,
|
||||||
|
url: String,
|
||||||
|
filesize: Number
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handle incremental updates of the mod_id
|
||||||
|
modSchema.pre('save', (next) => {
|
||||||
|
let doc = this;
|
||||||
|
database.counter.Counter.findByIdAndUpdate({_id: 'modId'}, {$inc: { seq: 1 }}, (error, counter) => {
|
||||||
|
if (error) {
|
||||||
|
return next(error)
|
||||||
|
}
|
||||||
|
doc.mod_id = counter.seq
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- Model ---
|
||||||
|
exports.Mod = mongoose.model('technicflux_mods', modSchema)
|
||||||
|
|
||||||
|
// --- Related Functions ---
|
||||||
|
|
||||||
|
exports.getModBySlug = (mSlug) => {
|
||||||
|
return exports.Mod.find({ name: mSlug }).exec().then((mods) => {
|
||||||
|
if (mods === null || mods.length === 0) {
|
||||||
|
// Mod not found
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
// Mod found
|
||||||
|
return {
|
||||||
|
id: mods[0].mod_id,
|
||||||
|
name: mods[0].name,
|
||||||
|
pretty_name: mods[0].pretty_name,
|
||||||
|
author: mods[0].author,
|
||||||
|
description: mods[0].description,
|
||||||
|
link: mods[0].link,
|
||||||
|
versions: mods.map((modVersion) => modVersion.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No mod found with this slug
|
||||||
|
debug(`ERROR (DB): Failed to find a mod with slug '${mSlug}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getModVersion = (mSlug, mVersion) => {
|
||||||
|
return exports.Mod.findOne({ name: mSlug, version: mVersion }).exec().then((mod) => {
|
||||||
|
// Mod found
|
||||||
|
return mod
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No mod found with this slug and version
|
||||||
|
debug(`ERROR (DB): Failed to find a mod '${mSlug}:${mVersion}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getModsByType = (mType) => {
|
||||||
|
return exports.Mod.find({ type: mType }).exec().then((mods) => {
|
||||||
|
// Mods found
|
||||||
|
return mods
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No mods found with this modloader
|
||||||
|
debug(`ERROR (DB): Failed to find a mod with modloader '${mType}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getModsByMinecraftVersion = (mVersion) => {
|
||||||
|
return exports.Mod.find({ mc_version: mVersion }).exec().then((mods) => {
|
||||||
|
// Mods found
|
||||||
|
return mods
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No mods found with this Minecraft version.
|
||||||
|
debug(`ERROR (DB): Failed to find a mod with Minecraft version '${mVersion}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getModsByMinecraftAndLoaderVersion = (mVersion, mType) => {
|
||||||
|
return exports.Mod.find({ mc_version: mVersion, type: mType }).exec().then((mods) => {
|
||||||
|
// Mods found
|
||||||
|
return mods
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No mods found with this Minecraft and modloader version.
|
||||||
|
debug(`ERROR (DB): Failed to find a mod with Minecraft version '${mVersion}' and modloader version '${mType}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getAllMods = () => {
|
||||||
|
return exports.Mod.find({}).exec().then((mods) => {
|
||||||
|
// Mods found
|
||||||
|
return mods
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No mods found
|
||||||
|
debug(`ERROR (DB): Failed to find any mods because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.updateMod = (mSlug, mObject) => {
|
||||||
|
return exports.Mod.updateOne(
|
||||||
|
{ name: mSlug },
|
||||||
|
{ $set: mObject }
|
||||||
|
).exec().then(() => {
|
||||||
|
// Successfully updated the modpack
|
||||||
|
return true
|
||||||
|
}).catch((reason) => {
|
||||||
|
// Failed to update the modpack
|
||||||
|
debug(`ERROR (DB): Failed to update modpack '${mSlug}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.deleteMod = (mSlug) => {
|
||||||
|
return exports.Mod.deleteOne({ name: mSlug }).exec().then(() => {
|
||||||
|
// Successfully deleted this mod
|
||||||
|
return true
|
||||||
|
}).catch((reason) => {
|
||||||
|
// Failed to delete the mod
|
||||||
|
debug(`ERROR (DB): Failed to delete mod '${mSlug}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
113
database/modpack.js
Normal file
113
database/modpack.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
const debug = require('debug')('technicflux:server')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
const { SchemaTypes } = require('mongoose')
|
||||||
|
const Schema = mongoose.Schema
|
||||||
|
|
||||||
|
// --- Schema ---
|
||||||
|
const modpackSchema = new Schema({
|
||||||
|
name: String,
|
||||||
|
display_name: String,
|
||||||
|
url: String,
|
||||||
|
recommended: String,
|
||||||
|
latest: String,
|
||||||
|
builds: [{ type: SchemaTypes.ObjectId, ref: 'technicflux_builds' }],
|
||||||
|
owners: [{ type: SchemaTypes.ObjectId, ref: 'technicflux_users' }],
|
||||||
|
contributors: [{ type: SchemaTypes.ObjectId, ref: 'technicflux_users' }],
|
||||||
|
admin: Boolean // whether the pack is owned by the admin user.
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- Model ---
|
||||||
|
exports.Modpack = mongoose.model('technicflux_modpacks', modpackSchema)
|
||||||
|
|
||||||
|
// --- Related Functions ---
|
||||||
|
|
||||||
|
exports.createModpack = (mSlug, mDisplayName, mOwner, mAdmin) => {
|
||||||
|
// Create a modpack object
|
||||||
|
const newModpack = new exports.Modpack({
|
||||||
|
name: mSlug,
|
||||||
|
display_name: mDisplayName,
|
||||||
|
owners: [mOwner],
|
||||||
|
admin: mAdmin
|
||||||
|
})
|
||||||
|
|
||||||
|
// Commit it to the database
|
||||||
|
return newModpack.save().then((modpack) => {
|
||||||
|
// Modpack successfully created
|
||||||
|
debug(`Successfully added new project ${modpack.display_name}: ${modpack.name}\n`)
|
||||||
|
return modpack
|
||||||
|
}).catch((reason) => {
|
||||||
|
// Modpack failed to create
|
||||||
|
debug(`ERROR (DB: when saving new modpack): ${reason}\n`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getModpackBySlug = (mSlug) => {
|
||||||
|
return exports.Modpack.findOne({ name: mSlug }).populate('owners').populate('contributors').populate('builds').exec().then((modpack) => {
|
||||||
|
// Modpack found
|
||||||
|
return modpack
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No modpack found with this slug
|
||||||
|
debug(`ERROR (DB): Failed to find a modpack with this slug because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getModpacksByOwner = (mOwner) => {
|
||||||
|
return exports.Modpack.find({ owners: mOwner }).populate('owners').populate('contributors').populate('builds').exec().then((modpacks) => {
|
||||||
|
// Modpacks found.
|
||||||
|
return modpacks
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No modpacks found for this owner.
|
||||||
|
debug(`ERROR (DB): Failed to find any modpacks with this owner because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getModpacksByContributor = (mContributor) => {
|
||||||
|
return exports.Modpack.find({ contributors: mContributor }).populate('owners').populate('contributors').populate('builds').exec().then((modpacks) => {
|
||||||
|
// Modpacks found.
|
||||||
|
return modpacks
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No modpacks found for this collaborator.
|
||||||
|
debug(`ERROR (DB): Failed to find any modpacks with this contributor because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getAllModpacks = () => {
|
||||||
|
// Fetch an array of all modpacks
|
||||||
|
return exports.Modpack.find({}).populate('owners').populate('contributors').populate('builds').exec().then((modpacks) => {
|
||||||
|
// Modpacks found
|
||||||
|
return modpacks
|
||||||
|
}).catch((reason) => {
|
||||||
|
// Failed to get any modpacks.
|
||||||
|
debug(`ERROR (DB): Failed to fetch any modpacks because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.updateModpack = (mSlug, mObject) => {
|
||||||
|
return exports.Modpack.updateOne(
|
||||||
|
{ name: mSlug },
|
||||||
|
{ $set: mObject }
|
||||||
|
).exec().then(() => {
|
||||||
|
// Successfully updated the modpack
|
||||||
|
return true
|
||||||
|
}).catch((reason) => {
|
||||||
|
// Failed to update the modpack
|
||||||
|
debug(`ERROR (DB): Failed to update modpack '${mSlug}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.deleteModpack = (mSlug) => {
|
||||||
|
return exports.Modpack.deleteOne({ name: mSlug }).exec().then(() => {
|
||||||
|
// Successfully deleted this modpack
|
||||||
|
return true
|
||||||
|
}).catch((reason) => {
|
||||||
|
// Failed to delete the modpack
|
||||||
|
debug(`ERROR (DB): Failed to delete modpack '${mSlug}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
192
database/user.js
Normal file
192
database/user.js
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
const debug = require('debug')('technicflux:server')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
const bcrypt = require('bcryptjs')
|
||||||
|
const Schema = mongoose.Schema
|
||||||
|
|
||||||
|
// --- Schema ---
|
||||||
|
const userSchema = new Schema({
|
||||||
|
username: {
|
||||||
|
type: String,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
display_name: String,
|
||||||
|
password: String,
|
||||||
|
created_on: Date,
|
||||||
|
login_history: [{ timestamp: Date, userAgent: String }]
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- Model ---
|
||||||
|
exports.User = mongoose.model('technicflux_users', userSchema)
|
||||||
|
|
||||||
|
// --- Related Functions ---
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a new user with the given information.
|
||||||
|
*/
|
||||||
|
exports.createUser = (uUsername, uDisplayName, uPassword) => {
|
||||||
|
// Encrypt the password using bcrypt and save user
|
||||||
|
return bcrypt.genSalt(10).then((Salt) => {
|
||||||
|
return bcrypt.hash(uPassword, Salt).then((hash) => {
|
||||||
|
// Create a user object
|
||||||
|
const newUser = new exports.User({
|
||||||
|
username: uUsername,
|
||||||
|
display_name: uDisplayName,
|
||||||
|
password: hash
|
||||||
|
})
|
||||||
|
|
||||||
|
// Commit it to the database
|
||||||
|
return newUser.save().then((user) => {
|
||||||
|
process.stdout.write(`Successfully added new user ${user.username}\n`)
|
||||||
|
return user
|
||||||
|
}).catch((reason) => {
|
||||||
|
process.stdout.write(`ERROR (when saving new user): ${reason}\n`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}).catch((err) => {
|
||||||
|
// Issue creating hashed password
|
||||||
|
process.stdout.write(`ERROR (while hashing password): ${err}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}).catch((err) => {
|
||||||
|
// Issue creating hashed object salt
|
||||||
|
process.stdout.write(`ERROR (while generating password salt): ${err}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets the user with the provided id.
|
||||||
|
*/
|
||||||
|
exports.getUserById = (uId) => {
|
||||||
|
// Fetch information about the user with the given id
|
||||||
|
return exports.User.findOne({ _id: uId }).exec().then((user) => {
|
||||||
|
// User found.
|
||||||
|
return user
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No user found with this id.
|
||||||
|
console.log(reason)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets the user with the provided username.
|
||||||
|
*/
|
||||||
|
exports.getUserByUsername = (uUsername) => {
|
||||||
|
// Fetch information about the user with the given username
|
||||||
|
return exports.User.findOne({ username: uUsername }).exec().then((user) => {
|
||||||
|
// User found.
|
||||||
|
return user
|
||||||
|
}).catch((reason) => {
|
||||||
|
// No user found with this username.
|
||||||
|
console.log(reason)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets an array of all existing users.
|
||||||
|
*/
|
||||||
|
exports.getAllUsers = () => {
|
||||||
|
// Fetch an array of all users
|
||||||
|
return exports.User.find({ }).exec().then((users) => {
|
||||||
|
// Users found.
|
||||||
|
return users
|
||||||
|
}).catch((reason) => {
|
||||||
|
console.log(reason)
|
||||||
|
// Error retrieving users.
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks if the corresponding password matches that of the provided username.
|
||||||
|
*/
|
||||||
|
exports.authUser = (uUsername, uPassword, logHistory = false, uAgent = 'Unknown') => {
|
||||||
|
// Fetch information about the user with the given username
|
||||||
|
return exports.User.findOne({ username: uUsername }).exec().then((user) => {
|
||||||
|
// User found. Check password.
|
||||||
|
return bcrypt.compare(uPassword, user.password).then(async (isMatch) => {
|
||||||
|
// Check if it is a match
|
||||||
|
if (isMatch) {
|
||||||
|
// Update the user's login history.
|
||||||
|
if (logHistory) {
|
||||||
|
const newLogin = { timestamp: new Date(), userAgent: String(uAgent) }
|
||||||
|
|
||||||
|
// Add the new login to the user's login history
|
||||||
|
return await exports.User.updateOne(
|
||||||
|
{ username: uUsername },
|
||||||
|
{ $push: { login_history: newLogin } }
|
||||||
|
).exec().then(() => {
|
||||||
|
// Auth was successful and login history updated.
|
||||||
|
user.login_history.push(newLogin)
|
||||||
|
return [true, user]
|
||||||
|
}).catch((reason) => {
|
||||||
|
user.login_history.push(newLogin)
|
||||||
|
// Auth was successful, but failed to update the user's login history.
|
||||||
|
process.stdout.write(`ERROR (while updating user's login history): ${reason}\n`)
|
||||||
|
return [true, user]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Auth was successful. Return user without logging to user's history.
|
||||||
|
return [true, user]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// User's authentication was invalid.
|
||||||
|
return [false, undefined]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch((reason) => {
|
||||||
|
console.log(reason)
|
||||||
|
// No user found with this username.
|
||||||
|
return [false]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updates the user with the given username.
|
||||||
|
*/
|
||||||
|
exports.updateUser = (uUsername, object) => {
|
||||||
|
return bcrypt.genSalt(10).then((Salt) => {
|
||||||
|
return bcrypt.hash(object.password, Salt).then((hash) => {
|
||||||
|
if (object.password.length > 0) {
|
||||||
|
object.password = hash
|
||||||
|
} else {
|
||||||
|
object.password = object.old_password
|
||||||
|
}
|
||||||
|
delete object.old_password
|
||||||
|
|
||||||
|
// Update User
|
||||||
|
return exports.User.updateOne(
|
||||||
|
{ username: uUsername },
|
||||||
|
{ $set: object }
|
||||||
|
).exec().then(() => {
|
||||||
|
// Updated user successfully
|
||||||
|
return true
|
||||||
|
}).catch((reason) => {
|
||||||
|
// Failed to update the user
|
||||||
|
process.stdout.write(`ERROR (while updating user): ${reason}\n`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}).catch((err) => {
|
||||||
|
// Issue creating hashed password
|
||||||
|
process.stdout.write(`ERROR (while hashing password): ${err}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}).catch((err) => {
|
||||||
|
// Issue creating hashed object salt
|
||||||
|
process.stdout.write(`ERROR (while generating password salt): ${err}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.deleteUser = (uId) => {
|
||||||
|
return exports.User.deleteOne({ _id: uId }).exec().then(() => {
|
||||||
|
// Successfully deleted this user.
|
||||||
|
return true
|
||||||
|
}).catch((reason) => {
|
||||||
|
// Failed to delete the user
|
||||||
|
debug(`ERROR (DB): Failed to delete user '${uId}' because of: ${reason}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
const express = require('express')
|
const express = require('express')
|
||||||
const rateLimit = require('express-rate-limit')
|
const rateLimit = require('express-rate-limit')
|
||||||
const MongoStore = require('rate-limit-mongo')
|
const MongoStore = require('rate-limit-mongo')
|
||||||
const database = require('../database')
|
const database = require('../database/database')
|
||||||
const branchName = require('current-git-branch')() || 'release'
|
const branchName = require('current-git-branch')() || 'release'
|
||||||
const pckg = require('../package.json')
|
const pckg = require('../package.json')
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
@@ -67,7 +67,7 @@ router.get('/mod/:modname/:modversion?', (req, res) => {
|
|||||||
const modName = req.params.modname
|
const modName = req.params.modname
|
||||||
const modVersion = req.params?.modversion
|
const modVersion = req.params?.modversion
|
||||||
|
|
||||||
return database.getModBySlug(`${modName}`).then((mod) => {
|
return database.mod.getModBySlug(`${modName}`).then((mod) => {
|
||||||
// Check if a mod was found
|
// Check if a mod was found
|
||||||
if (mod === null) {
|
if (mod === null) {
|
||||||
// Mod Not Found
|
// Mod Not Found
|
||||||
@@ -79,7 +79,7 @@ router.get('/mod/:modname/:modversion?', (req, res) => {
|
|||||||
return res.json(mod)
|
return res.json(mod)
|
||||||
} else {
|
} else {
|
||||||
// Request is for a particular version.
|
// Request is for a particular version.
|
||||||
return database.getModVersion(modName, modVersion).then((version) => {
|
return database.mod.getModVersion(modName, modVersion).then((version) => {
|
||||||
if (version === null) {
|
if (version === null) {
|
||||||
// Version Not Found
|
// Version Not Found
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
@@ -111,7 +111,7 @@ router.get('/mod/:modname/:modversion?', (req, res) => {
|
|||||||
* Purpose: Displays a list of all modpacks that are on this TechnicFlux instance.
|
* Purpose: Displays a list of all modpacks that are on this TechnicFlux instance.
|
||||||
*/
|
*/
|
||||||
router.get('/modpack', (req, res) => {
|
router.get('/modpack', (req, res) => {
|
||||||
return database.getAllModpacks().then((modpacks) => {
|
return database.modpack.getAllModpacks().then((modpacks) => {
|
||||||
// Modpack Index
|
// Modpack Index
|
||||||
return res.json({
|
return res.json({
|
||||||
modpacks: (modpacks === null)
|
modpacks: (modpacks === null)
|
||||||
@@ -137,7 +137,7 @@ router.get('/modpack/:slug/:build?', (req, res) => {
|
|||||||
const slug = req.params.slug
|
const slug = req.params.slug
|
||||||
const build = req.params?.build
|
const build = req.params?.build
|
||||||
|
|
||||||
return database.getModpackBySlug(`${slug}`).then((modpack) => {
|
return database.modpack.getModpackBySlug(`${slug}`).then((modpack) => {
|
||||||
if (modpack === null) {
|
if (modpack === null) {
|
||||||
// Modpack Not Found
|
// Modpack Not Found
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
@@ -192,7 +192,7 @@ router.get('/verify/:key?', (req, res) => {
|
|||||||
const apiKey = req.params?.key
|
const apiKey = req.params?.key
|
||||||
|
|
||||||
if (apiKey !== undefined) {
|
if (apiKey !== undefined) {
|
||||||
database.authAPIKey(apiKey).then((key) => {
|
database.apiKey.authAPIKey(apiKey).then((key) => {
|
||||||
if (apiKey === process.env?.API_KEY) {
|
if (apiKey === process.env?.API_KEY) {
|
||||||
// Key validated
|
// Key validated
|
||||||
return res.json({
|
return res.json({
|
||||||
|
|||||||
Reference in New Issue
Block a user