Compare commits
5 Commits
2318d22f38
...
d6779a7323
Author | SHA1 | Date | |
---|---|---|---|
d6779a7323 | |||
be2511c01b | |||
5c9171363c | |||
d33ef3a3f5 | |||
6e430db6e1 |
@ -25,10 +25,11 @@ const User = ({user}) => {
|
|||||||
const Restricted = ({user}) => {
|
const Restricted = ({user}) => {
|
||||||
|
|
||||||
if (!user) return '';
|
if (!user) return '';
|
||||||
|
const { upload, admin } = user.permissions;
|
||||||
return (
|
return (
|
||||||
<div className='flex-container'>
|
<div className='flex-container'>
|
||||||
{user.admin ? <NavLink className='navlink' to='/panel' >Panel</NavLink> : ''}
|
{admin ? <NavLink className='navlink' to='/panel' >Panel</NavLink> : ''}
|
||||||
<NavLink className='navlink' to='/upload' >Upload</NavLink>
|
{upload || admin ? <NavLink className='navlink' to='/upload' >Upload</NavLink>: '' }
|
||||||
<User user={user} />
|
<User user={user} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
.panel {
|
.panel {
|
||||||
height: inherit;
|
height: inherit;
|
||||||
width: 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';
|
import '../css/Panel.css';
|
||||||
|
|
||||||
|
const User = ({user}) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='user flex-container'>
|
||||||
|
{user.tag}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
const Panel = () => {
|
const Panel = () => {
|
||||||
|
|
||||||
|
const [users, setUsers] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const response = await fetch('/api/users');
|
||||||
|
if (response.status !== 200) return;
|
||||||
|
const users = await response.json();
|
||||||
|
setUsers(users);
|
||||||
|
})();
|
||||||
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='panel'>
|
<div className='panel flex-container'>
|
||||||
asd
|
{users.length ? users.map(user => <User key={user.id} user={user}/>):'No users'}
|
||||||
</div>
|
</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 Intercom = require('./Intercom');
|
||||||
const Registry = require('./Registry.js');
|
const Registry = require('./Registry.js');
|
||||||
const ClipIndex = require('./ClipIndex');
|
const ClipIndex = require('./ClipIndex');
|
||||||
|
const Database = require('./Database.js');
|
||||||
|
const Users = require('./Users');
|
||||||
|
|
||||||
class Client extends EventEmitter {
|
class Client extends EventEmitter {
|
||||||
|
|
||||||
@ -42,9 +44,12 @@ class Client extends EventEmitter {
|
|||||||
this.mediaDirectory = path.join(this.baseDirectory, opts.media.videos);
|
this.mediaDirectory = path.join(this.baseDirectory, opts.media.videos);
|
||||||
|
|
||||||
this.registry = new Registry(this);
|
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.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.logger = new Logger(this);
|
||||||
this.clipIndex = new ClipIndex(this, opts);
|
this.clipIndex = new ClipIndex(this, opts);
|
||||||
this.server = null;
|
this.server = null;
|
||||||
@ -61,6 +66,11 @@ class Client extends EventEmitter {
|
|||||||
this.passport = passport;
|
this.passport = passport;
|
||||||
this.app = express();
|
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
|
// Shouldn't be necessary, everything should come from the same domain: galactic.corgi.wtf
|
||||||
this.app.use(cors({
|
this.app.use(cors({
|
||||||
origin: (origin, cb) => {
|
origin: (origin, cb) => {
|
||||||
@ -121,8 +131,8 @@ class Client extends EventEmitter {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
passport.serializeUser((user, callback) => {
|
passport.serializeUser(async (user, callback) => {
|
||||||
user.admin = user.id === '132777808362471424';
|
user = await this.users.getOrCreate(user);
|
||||||
callback(null, user);
|
callback(null, user);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -130,11 +140,6 @@ class Client extends EventEmitter {
|
|||||||
callback(null, user);
|
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.app.use((req, res, next) => {
|
||||||
this.logger.debug(`New request to path ${req.path} || Route: ${req.route}`);
|
this.logger.debug(`New request to path ${req.path} || Route: ${req.route}`);
|
||||||
res.once('finish', () => {
|
res.once('finish', () => {
|
||||||
@ -148,12 +153,22 @@ class Client extends EventEmitter {
|
|||||||
next();
|
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() {
|
async init() {
|
||||||
|
|
||||||
this.logger.info('Loading endpoints');
|
this.logger.info('Loading endpoints');
|
||||||
await this.registry.init();
|
await this.registry.init();
|
||||||
|
await this.database.init();
|
||||||
this.logger.debug(this.registry.print);
|
this.logger.debug(this.registry.print);
|
||||||
const httpOpts = {
|
const httpOpts = {
|
||||||
port: this.port,
|
port: this.port,
|
||||||
@ -169,8 +184,6 @@ class Client extends EventEmitter {
|
|||||||
this.server = http.createServer(httpOpts, this.app).listen(this.port);
|
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.ready = true;
|
||||||
this.logger.info(`API client built in ${process.env.NODE_ENV} environment`);
|
this.logger.info(`API client built in ${process.env.NODE_ENV} environment`);
|
||||||
process.send({ _ready: true });
|
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 = {
|
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_DB_URL: env.API_DB_URL,
|
||||||
API_SESSION_STORE: env.API_SESSION_STORE,
|
API_SESSION_STORE: env.API_SESSION_STORE,
|
||||||
API_SESSION_COLLECTION: env.API_SESSION_COLLECTION,
|
API_SESSION_COLLECTION: env.API_SESSION_COLLECTION,
|
||||||
|
API_USER_COLLECTION: env.API_USER_COLLECTION,
|
||||||
HTTP_PORT: opts.http.port,
|
HTTP_PORT: opts.http.port,
|
||||||
API_SECRET: env.API_SECRET,
|
API_SECRET: env.API_SECRET,
|
||||||
// DASHBOARD: opts.dasboardUrl,
|
|
||||||
AUTH_CALLBACK: opts.discord.callback,
|
AUTH_CALLBACK: opts.discord.callback,
|
||||||
DISCORD_SECRET: env.DISCORD_SECRET,
|
DISCORD_SECRET: env.DISCORD_SECRET,
|
||||||
DISCORD_ID: env.DISCORD_ID,
|
DISCORD_ID: env.DISCORD_ID,
|
||||||
|
Loading…
Reference in New Issue
Block a user