Compare commits
5 Commits
2318d22f38
...
d6779a7323
Author | SHA1 | Date | |
---|---|---|---|
d6779a7323 | |||
be2511c01b | |||
5c9171363c | |||
d33ef3a3f5 | |||
6e430db6e1 |
@ -25,10 +25,11 @@ const User = ({user}) => {
|
||||
const Restricted = ({user}) => {
|
||||
|
||||
if (!user) return '';
|
||||
const { upload, admin } = user.permissions;
|
||||
return (
|
||||
<div className='flex-container'>
|
||||
{user.admin ? <NavLink className='navlink' to='/panel' >Panel</NavLink> : ''}
|
||||
<NavLink className='navlink' to='/upload' >Upload</NavLink>
|
||||
{admin ? <NavLink className='navlink' to='/panel' >Panel</NavLink> : ''}
|
||||
{upload || admin ? <NavLink className='navlink' to='/upload' >Upload</NavLink>: '' }
|
||||
<User user={user} />
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
.panel {
|
||||
height: inherit;
|
||||
width: inherit;
|
||||
flex-direction: column;
|
||||
}
|
@ -1,15 +1,34 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import '../css/Panel.css';
|
||||
|
||||
const User = ({user}) => {
|
||||
|
||||
return (
|
||||
<div className='user flex-container'>
|
||||
{user.tag}
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
const Panel = () => {
|
||||
|
||||
const [users, setUsers] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
(async () => {
|
||||
const response = await fetch('/api/users');
|
||||
if (response.status !== 200) return;
|
||||
const users = await response.json();
|
||||
setUsers(users);
|
||||
})();
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className='panel'>
|
||||
asd
|
||||
<div className='panel flex-container'>
|
||||
{users.length ? users.map(user => <User key={user.id} user={user}/>):'No users'}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
212
server/src/client/Database.js
Normal file
212
server/src/client/Database.js
Normal file
@ -0,0 +1,212 @@
|
||||
const { Logger } = require('../util/');
|
||||
const { MongoClient } = require('mongodb');
|
||||
|
||||
/**
|
||||
* A dedicated class to locally wrap the mongodb wrapper
|
||||
*
|
||||
* @class MongoDB
|
||||
*/
|
||||
class MongoDB {
|
||||
|
||||
constructor(client, config) {
|
||||
|
||||
if (!client) throw new Error('Missing reference to client!');
|
||||
if (!config) throw new Error('No config file provided!');
|
||||
if (config && (!config.database || !config.url)) throw new Error('Invalid config file provided!');
|
||||
|
||||
this.config = config;
|
||||
this.client = null; // Mongo Client
|
||||
this.parent = client; // Parent client
|
||||
this.db = null;
|
||||
|
||||
this.logger = new Logger(this);
|
||||
this.logger._debug = this.parent._debug;
|
||||
|
||||
}
|
||||
|
||||
async init() {
|
||||
|
||||
this.logger.info('Initializing database connection.');
|
||||
|
||||
try {
|
||||
|
||||
const client = new MongoClient(this.config.url + this.config.database, { useUnifiedTopology: true });
|
||||
this.client = await client.connect();
|
||||
this.db = await this.client.db(this.config.database);
|
||||
this.logger.info('Database connected.');
|
||||
|
||||
} catch (err) {
|
||||
|
||||
this.logger.error('Database connection failed!\n' + err);
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the first match
|
||||
*
|
||||
* @param {String} db The collection in which the data is to be updated
|
||||
* @param {Object} query The filter that is used to find the data
|
||||
* @returns {Array} An array containing the corresponding objects for the query
|
||||
* @memberof Database
|
||||
*/
|
||||
async find(db, query) {
|
||||
|
||||
this.logger.debug(`Incoming find query for ${db} with parameters ${JSON.stringify(query)}`);
|
||||
|
||||
const cursor = await this.db.collection(db).find(query);
|
||||
return cursor.toArray();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the first match
|
||||
*
|
||||
* @param {String} db The collection in which the data is to be updated
|
||||
* @param {Object} query The filter that is used to find the data
|
||||
* @returns {Object} An object containing the queried data
|
||||
* @memberof Database
|
||||
*/
|
||||
findOne(db, query) {
|
||||
|
||||
this.logger.debug(`Incoming findOne query for ${db} with parameters ${JSON.stringify(query)}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
this.db.collection(db).findOne(query, async (error, item) => {
|
||||
|
||||
if (error) return reject(error);
|
||||
return resolve(item);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any and all filter matches.
|
||||
* DEPRECATED!
|
||||
*
|
||||
* @param {String} db The collection in which the data is to be updated
|
||||
* @param {Object} filter The filter that is used to find the data
|
||||
* @param {Object} data The updated data
|
||||
* @returns {WriteResult} Object containing the followint counts: Matched, Upserted, Modified
|
||||
* @memberof Database
|
||||
*/
|
||||
update(db, filter, data) {
|
||||
|
||||
this.logger.debug(`Incoming update query for '${db}' with parameters\n${JSON.stringify(filter)}\nand data\n${JSON.stringify(data)}`);
|
||||
this.logger.warn('Database.update() is deprecated!');
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
this.db.collection(db).update(filter, data, async (error, result) => {
|
||||
if (error) return reject(error);
|
||||
return resolve(result);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the first filter match.
|
||||
*
|
||||
* @param {String} db The collection in which the data is to be updated
|
||||
* @param {Object} filter The filter that is used to find the data
|
||||
* @param {Object} data The updated data
|
||||
* @returns {WriteResult} Object containing the followint counts: Matched, Upserted, Modified
|
||||
* @memberof Database
|
||||
*/
|
||||
updateOne(db, filter, data, upsert = false) {
|
||||
|
||||
this.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
this.db.collection(db).updateOne(filter, { $set: data }, { upsert }, async (error, result) => {
|
||||
|
||||
if (error) return reject(error);
|
||||
|
||||
//return resolve(result)
|
||||
const { matchedCount, upsertedCount, modifiedCount } = result;
|
||||
return resolve({ matched: matchedCount, upserted: upsertedCount, modified: modifiedCount });
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Push data to an array
|
||||
*
|
||||
* @param {string} db The collection to query
|
||||
* @param {object} filter The filter to find the document to update
|
||||
* @param {object} data The data to be pushed
|
||||
* @param {boolean} [upsert=false]
|
||||
* @returns
|
||||
* @memberof Database
|
||||
*/
|
||||
push(db, filter, data, upsert = false) {
|
||||
|
||||
this.logger.debug(`Incoming push query for ${db}, with upsert ${upsert} and with parameters ${JSON.stringify(filter)} and data ${JSON.stringify(data)}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
this.db.collection(db).updateOne(filter, { $push: data }, { upsert }, async (error, result) => {
|
||||
|
||||
if (error) return reject(error);
|
||||
return resolve(result);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a random element from a database
|
||||
*
|
||||
* @param {string} db The collection to query
|
||||
* @param {object} [filter={}] The filtering object to narrow down the sample pool
|
||||
* @param {number} [amount=1] Amount of items to return
|
||||
* @returns {object}
|
||||
* @memberof Database
|
||||
*/
|
||||
random(db, filter = {}, amount = 1) {
|
||||
|
||||
this.logger.debug(`Incoming random query for ${db} with parameters ${JSON.stringify(filter)} and amount ${amount}`);
|
||||
if (amount > 100) amount = 100;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
this.db.collection(db).aggregate([{ $match: filter }, { $sample: { size: amount } }], (err, item) => {
|
||||
|
||||
if (err) return reject(err);
|
||||
resolve(item);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
stats(options = {}) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if (!this.db) return reject(new Error('Database not initialised'));
|
||||
|
||||
this.db.stats(options, (error, result) => {
|
||||
if (error) reject(error);
|
||||
resolve(result);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = MongoDB;
|
@ -17,6 +17,8 @@ const { Logger } = require('../util');
|
||||
const Intercom = require('./Intercom');
|
||||
const Registry = require('./Registry.js');
|
||||
const ClipIndex = require('./ClipIndex');
|
||||
const Database = require('./Database.js');
|
||||
const Users = require('./Users');
|
||||
|
||||
class Client extends EventEmitter {
|
||||
|
||||
@ -42,9 +44,12 @@ class Client extends EventEmitter {
|
||||
this.mediaDirectory = path.join(this.baseDirectory, opts.media.videos);
|
||||
|
||||
this.registry = new Registry(this);
|
||||
// this.mongoDB = new MongoDB(this, this._mongoOpts);
|
||||
this.database = new Database(this, this._mongoOpts);
|
||||
this.intercom = new Intercom(this);
|
||||
// this.permissionsUtil = PermissionsUtil;
|
||||
this.users = new Users(this, {
|
||||
database: this.database,
|
||||
collection: env.API_USER_COLLECTION
|
||||
});
|
||||
this.logger = new Logger(this);
|
||||
this.clipIndex = new ClipIndex(this, opts);
|
||||
this.server = null;
|
||||
@ -61,6 +66,11 @@ class Client extends EventEmitter {
|
||||
this.passport = passport;
|
||||
this.app = express();
|
||||
|
||||
this.app.use((req, res, next) => {
|
||||
if (!this.ready) return res.status(503).send('Server not ready. Try again in a moment.');
|
||||
next();
|
||||
});
|
||||
|
||||
// Shouldn't be necessary, everything should come from the same domain: galactic.corgi.wtf
|
||||
this.app.use(cors({
|
||||
origin: (origin, cb) => {
|
||||
@ -121,8 +131,8 @@ class Client extends EventEmitter {
|
||||
}
|
||||
));
|
||||
|
||||
passport.serializeUser((user, callback) => {
|
||||
user.admin = user.id === '132777808362471424';
|
||||
passport.serializeUser(async (user, callback) => {
|
||||
user = await this.users.getOrCreate(user);
|
||||
callback(null, user);
|
||||
});
|
||||
|
||||
@ -130,11 +140,6 @@ class Client extends EventEmitter {
|
||||
callback(null, user);
|
||||
});
|
||||
|
||||
this.app.use((req, res, next) => {
|
||||
if (!this.ready) return res.status(503).send('Server not ready. Try again in a moment.');
|
||||
next();
|
||||
});
|
||||
|
||||
this.app.use((req, res, next) => {
|
||||
this.logger.debug(`New request to path ${req.path} || Route: ${req.route}`);
|
||||
res.once('finish', () => {
|
||||
@ -148,12 +153,22 @@ class Client extends EventEmitter {
|
||||
next();
|
||||
});
|
||||
|
||||
this.users.on('userCreate', (user) => {
|
||||
this.logger.info(`New user created: ${user.tag}`);
|
||||
});
|
||||
|
||||
this.users.on('debug', (msg) => {
|
||||
this.logger.debug(msg);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
async init() {
|
||||
|
||||
this.logger.info('Loading endpoints');
|
||||
await this.registry.init();
|
||||
await this.database.init();
|
||||
this.logger.debug(this.registry.print);
|
||||
const httpOpts = {
|
||||
port: this.port,
|
||||
@ -169,8 +184,6 @@ class Client extends EventEmitter {
|
||||
this.server = http.createServer(httpOpts, this.app).listen(this.port);
|
||||
}
|
||||
|
||||
// await this.mongoDB.init().catch((err) => this.logger.error(err.stack));
|
||||
|
||||
this.ready = true;
|
||||
this.logger.info(`API client built in ${process.env.NODE_ENV} environment`);
|
||||
process.send({ _ready: true });
|
||||
|
49
server/src/client/Users.js
Normal file
49
server/src/client/Users.js
Normal file
@ -0,0 +1,49 @@
|
||||
const { EventEmitter } = require('events');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const MongoDB = require('./Database');
|
||||
|
||||
class Users extends EventEmitter {
|
||||
|
||||
|
||||
/**
|
||||
* Creates an instance of Users.
|
||||
* @param {*} client
|
||||
* @param {MongoDB} database
|
||||
* @memberof Users
|
||||
*/
|
||||
constructor(client, { database, collection }) {
|
||||
|
||||
super();
|
||||
|
||||
this.client = client;
|
||||
this.database = database;
|
||||
this.collection = collection;
|
||||
|
||||
}
|
||||
|
||||
async getOrCreate(user) {
|
||||
|
||||
const id = user.id || user;
|
||||
this.emit('debug', `User perms query for ${id}`);
|
||||
const userPartial = await this.database.findOne(this.collection, { id });
|
||||
user = { ...user, ...userPartial };
|
||||
user.tag = `${user.username}#${user.discriminator}`;
|
||||
this.emit('debug', `Result for ${id}: ${JSON.stringify(userPartial)}`);
|
||||
if (userPartial) return user;
|
||||
|
||||
user.permissions = {};
|
||||
await this.database.updateOne(this.collection, { id }, { id, tag: user.tag, permissions: {} }, true);
|
||||
this.emit('userCreate', user);
|
||||
return user;
|
||||
|
||||
}
|
||||
|
||||
getAll() {
|
||||
|
||||
return this.database.find(this.collection, {});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Users;
|
46
server/src/client/endpoints/api/Users.js
Normal file
46
server/src/client/endpoints/api/Users.js
Normal file
@ -0,0 +1,46 @@
|
||||
const { APIEndpoint } = require('../../interfaces');
|
||||
const { CheckAuth, Permissions } = require('../../middleware');
|
||||
|
||||
class Users extends APIEndpoint {
|
||||
|
||||
constructor(client, opts) {
|
||||
|
||||
super(client, {
|
||||
name: 'users',
|
||||
path: '/users',
|
||||
...opts
|
||||
});
|
||||
|
||||
this.methods = [
|
||||
['get', this.get.bind(this)]
|
||||
];
|
||||
|
||||
this.subpaths = [
|
||||
['/:id', 'get', this.user.bind(this)]
|
||||
];
|
||||
|
||||
this.middleware = [CheckAuth, Permissions('admin')];
|
||||
|
||||
this.init();
|
||||
|
||||
}
|
||||
|
||||
async get(req, res) {
|
||||
|
||||
try {
|
||||
const users = await this.client.users.getAll();
|
||||
res.json(users);
|
||||
} catch (err) {
|
||||
this.logger.error(err.stack || err);
|
||||
res.status(500).end();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async user(req, res) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Users;
|
12
server/src/client/middleware/Permissions.js
Normal file
12
server/src/client/middleware/Permissions.js
Normal file
@ -0,0 +1,12 @@
|
||||
const Permissions = (perm) => {
|
||||
|
||||
return (req, res, next) => {
|
||||
const { user: { permissions } } = req;
|
||||
if (permissions[perm]) return next();
|
||||
res.status(401).end();
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
module.exports = Permissions;
|
@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
CheckAuth: require('./Auth.js')
|
||||
CheckAuth: require('./Auth.js'),
|
||||
Permissions: require('./Permissions.js'),
|
||||
};
|
@ -138,9 +138,9 @@ class Manager extends EventEmitter {
|
||||
API_DB_URL: env.API_DB_URL,
|
||||
API_SESSION_STORE: env.API_SESSION_STORE,
|
||||
API_SESSION_COLLECTION: env.API_SESSION_COLLECTION,
|
||||
API_USER_COLLECTION: env.API_USER_COLLECTION,
|
||||
HTTP_PORT: opts.http.port,
|
||||
API_SECRET: env.API_SECRET,
|
||||
// DASHBOARD: opts.dasboardUrl,
|
||||
AUTH_CALLBACK: opts.discord.callback,
|
||||
DISCORD_SECRET: env.DISCORD_SECRET,
|
||||
DISCORD_ID: env.DISCORD_ID,
|
||||
|
Loading…
Reference in New Issue
Block a user