mirror of
https://github.com/PretendoNetwork/juxtaposition-ui.git
synced 2026-04-25 08:04:32 -05:00
Added Admin panel to manage Communities, Discovery, and view users, as well as their respective API endpoints
This commit is contained in:
parent
d11b04efad
commit
daed45e535
9
package-lock.json
generated
9
package-lock.json
generated
|
|
@ -340,6 +340,15 @@
|
|||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||
},
|
||||
"cookie-parser": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
|
||||
"integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
|
||||
"requires": {
|
||||
"cookie": "0.4.0",
|
||||
"cookie-signature": "1.0.6"
|
||||
}
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"dependencies": {
|
||||
"body-parser": "^1.19.0",
|
||||
"colors": "^1.4.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"express": "^4.17.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"memory-cache": "^0.2.0",
|
||||
|
|
|
|||
|
|
@ -122,8 +122,6 @@ let methods = {
|
|||
}
|
||||
catch(e)
|
||||
{
|
||||
console.log(e);
|
||||
console.error("The token was incorrect");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -200,5 +198,18 @@ let methods = {
|
|||
let pngBuffer = PNG.sync.write(png);
|
||||
return `data:image/png;base64,${pngBuffer.toString('base64')}`;
|
||||
},
|
||||
nintendoPasswordHash: function(password, pid) {
|
||||
const pidBuffer = Buffer.alloc(4);
|
||||
pidBuffer.writeUInt32LE(pid);
|
||||
|
||||
const unpacked = Buffer.concat([
|
||||
pidBuffer,
|
||||
Buffer.from('\x02\x65\x43\x46'),
|
||||
Buffer.from(password)
|
||||
]);
|
||||
const hashed = crypto.createHash('sha256').update(unpacked).digest().toString('hex');
|
||||
|
||||
return hashed;
|
||||
},
|
||||
};
|
||||
exports.data = methods;
|
||||
|
|
@ -13,5 +13,8 @@
|
|||
"useNewUrlParser": true,
|
||||
"useUnifiedTopology": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"authorized_PNIDs" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
|
|
@ -48,7 +48,10 @@ async function getTopicByCommunityID(communityID) {
|
|||
|
||||
async function getCommunities(numberOfCommunities) {
|
||||
verifyConnected();
|
||||
return COMMUNITY.find({}).limit(numberOfCommunities);
|
||||
if(numberOfCommunities === -1)
|
||||
return COMMUNITY.find({});
|
||||
else
|
||||
return COMMUNITY.find({}).limit(numberOfCommunities);
|
||||
}
|
||||
|
||||
async function getMostPopularCommunities(numberOfCommunities) {
|
||||
|
|
@ -71,7 +74,7 @@ async function getCommunityByTitleID(title_id) {
|
|||
async function getCommunityByID(community_id) {
|
||||
verifyConnected();
|
||||
return COMMUNITY.findOne({
|
||||
id: community_id
|
||||
community_id: community_id
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -154,6 +157,14 @@ async function getDiscoveryHosts() {
|
|||
});
|
||||
}
|
||||
|
||||
async function getUsers(numberOfUsers) {
|
||||
verifyConnected();
|
||||
if(numberOfUsers === -1)
|
||||
return USER.find({});
|
||||
else
|
||||
return USER.find({}).limit(numberOfUsers);
|
||||
}
|
||||
|
||||
async function getUserByPID(PID) {
|
||||
verifyConnected();
|
||||
|
||||
|
|
@ -162,11 +173,17 @@ async function getUserByPID(PID) {
|
|||
});
|
||||
}
|
||||
|
||||
async function getUserByUsername(user_id) {
|
||||
verifyConnected();
|
||||
|
||||
return USER.findOne({
|
||||
user_id: new RegExp(`^${user_id}$`, 'i')
|
||||
});
|
||||
}
|
||||
|
||||
async function getServerConfig() {
|
||||
verifyConnected();
|
||||
return ENDPOINT.findOne({
|
||||
type: "config"
|
||||
});
|
||||
return ENDPOINT.findOne();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
@ -186,6 +203,9 @@ module.exports = {
|
|||
getNumberUserPostsByID,
|
||||
getTotalPostsByUserID,
|
||||
getPostByID,
|
||||
getUsers,
|
||||
getUserByPID,
|
||||
getUserByUsername,
|
||||
getUserPostsAfterTimestamp,
|
||||
getServerConfig
|
||||
};
|
||||
|
|
@ -7,6 +7,8 @@ const router = express.Router();
|
|||
|
||||
const portal = express.Router();
|
||||
const ctr = express.Router();
|
||||
const admin = express.Router();
|
||||
const web_api = express.Router();
|
||||
|
||||
// Create subdomains
|
||||
logger.info('[JUXT-WEB] Creating \'Wii U\' subdomain');
|
||||
|
|
@ -15,6 +17,12 @@ router.use(subdomain('portal.olv', portal));
|
|||
logger.info('[JUXT-WEB] Creating \'3DS\' subdomain');
|
||||
router.use(subdomain('ctr.olv', ctr));
|
||||
|
||||
logger.info('[JUXT-WEB] Creating \'Admin\' subdomain');
|
||||
router.use(subdomain('admin.olv', admin));
|
||||
|
||||
logger.info('[JUXT-WEB] Creating \'Web API\' subdomain');
|
||||
router.use(subdomain('web_api.olv', admin));
|
||||
|
||||
// Setup routes
|
||||
portal.use('/titles/show', routes.PORTAL_SHOW);
|
||||
portal.use('/communities', routes.PORTAL_COMMUNITIES);
|
||||
|
|
@ -27,4 +35,7 @@ ctr.use('/communities', routes.CTR_COMMUNITIES);
|
|||
ctr.use('/users', routes.CTR_USER);
|
||||
ctr.use('/', routes.CTR_WEB);
|
||||
|
||||
admin.use('/', routes.WEB_ADMIN);
|
||||
admin.use('/v1/', routes.WEB_API);
|
||||
|
||||
module.exports = router;
|
||||
275
src/services/juxt-web/routes/admin/api.js
Normal file
275
src/services/juxt-web/routes/admin/api.js
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
var express = require('express');
|
||||
const database = require('../../../../database');
|
||||
const util = require('../../../../authentication');
|
||||
const config = require('../../../../config.json');
|
||||
const { COMMUNITY } = require('../../../../models/communities');
|
||||
var router = express.Router();
|
||||
const moment = require('moment');
|
||||
var multer = require('multer');
|
||||
const snowflake = require('node-snowflake').Snowflake;
|
||||
var storage = multer.memoryStorage();
|
||||
var upload = multer({ storage: storage });
|
||||
|
||||
router.get('/communities/all', function (req, res) {
|
||||
database.connect().then(async e => {
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
throw new Error('No service token supplied');
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
|
||||
if(pid === null)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
let user = await database.getUserByPID(pid);
|
||||
|
||||
if(user !== null)
|
||||
{
|
||||
if(config.authorized_PNIDs.indexOf(user.pid) === -1)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
res.send(await database.getCommunities(-1))
|
||||
}
|
||||
else
|
||||
throw new Error('Invalid account ID or password');
|
||||
|
||||
}).catch(error =>
|
||||
{
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
error_code: 400,
|
||||
message: error.message
|
||||
};
|
||||
res.send(response);
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/communities/:communityID', function (req, res) {
|
||||
database.connect().then(async e => {
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
throw new Error('No service token supplied');
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
|
||||
if(pid === null)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
let user = await database.getUserByPID(pid);
|
||||
|
||||
if(user !== null)
|
||||
{
|
||||
if(config.authorized_PNIDs.indexOf(user.pid) === -1)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
res.send(await database.getCommunityByID(req.params.communityID))
|
||||
}
|
||||
else
|
||||
throw new Error('Invalid account ID or password');
|
||||
|
||||
}).catch(error =>
|
||||
{
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
error_code: 400,
|
||||
message: error.message
|
||||
};
|
||||
res.send(response);
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/communities/:communityID/update', upload.fields([{name: 'browserIcon', maxCount: 1}, { name: 'CTRbrowserHeader', maxCount: 1}, { name: 'WiiUbrowserHeader', maxCount: 1}]), function (req, res) {
|
||||
database.connect().then(async e => {
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
throw new Error('No service token supplied');
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
|
||||
if(pid === null)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
let user = await database.getUserByPID(pid);
|
||||
|
||||
if(user !== null)
|
||||
{
|
||||
if(config.authorized_PNIDs.indexOf(user.pid) === -1)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
const community = await database.getCommunityByID(req.params.communityID);
|
||||
const files = JSON.parse(JSON.stringify(req.files));
|
||||
|
||||
if(req.body.name && community.name !== req.body.name)
|
||||
community.name = req.body.name;
|
||||
|
||||
if(req.body.description && community.description !== req.body.description)
|
||||
community.description = req.body.description;
|
||||
|
||||
if(req.body.title_ids[0] !== community.title_ids[0] && req.body.title_ids[1] !== community.title_ids[1] && req.body.title_ids[2] !== community.title_ids[2]
|
||||
&& req.body.title_ids[0] !== '' && req.body.title_ids[1] !== '' && req.body.title_ids[2] !== '') {
|
||||
community.title_id = req.body.title_ids;
|
||||
community.title_ids = req.body.title_ids;
|
||||
}
|
||||
|
||||
if(req.body.icon && community.icon !== req.body.icon)
|
||||
community.icon = req.body.icon;
|
||||
|
||||
if(req.files.browserIcon)
|
||||
community.browser_icon = `data:image/png;base64,${req.files.browserIcon[0].buffer.toString('base64')}`;
|
||||
|
||||
if(req.files.CTRbrowserHeader)
|
||||
community.CTR_browser_header = `data:image/png;base64,${req.files.CTRbrowserHeader[0].buffer.toString('base64')}`;
|
||||
|
||||
if(req.files.WiiUbrowserHeader)
|
||||
community.WiiU_browser_header = `data:image/png;base64,${req.files.WiiUbrowserHeader[0].buffer.toString('base64')}`;
|
||||
|
||||
if(req.body.is_recommended)
|
||||
community.is_recommended = req.body.is_recommended;
|
||||
|
||||
if(req.body.has_shop_page)
|
||||
community.has_shop_page = req.body.has_shop_page;
|
||||
|
||||
if(req.body.platform_id)
|
||||
community.platform_id = req.body.platform_id;
|
||||
|
||||
community.save();
|
||||
res.sendStatus(200);
|
||||
}
|
||||
else
|
||||
throw new Error('Invalid account ID or password');
|
||||
|
||||
}).catch(error =>
|
||||
{
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
error_code: 400,
|
||||
message: error.message
|
||||
};
|
||||
res.send(response);
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/communities/new', upload.fields([{name: 'browserIcon', maxCount: 1}, { name: 'CTRbrowserHeader', maxCount: 1}, { name: 'WiiUbrowserHeader', maxCount: 1}]), function (req, res) {
|
||||
database.connect().then(async e => {
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
throw new Error('No service token supplied');
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
|
||||
if(pid === null)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
let user = await database.getUserByPID(pid);
|
||||
|
||||
if(user !== null)
|
||||
{
|
||||
if(config.authorized_PNIDs.indexOf(user.pid) === -1)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
JSON.parse(JSON.stringify(req.files));
|
||||
const document = {
|
||||
empathy_count: 0,
|
||||
id: snowflake.nextId(),
|
||||
has_shop_page: req.body.has_shop_page,
|
||||
platform_id: req.body.platform_ID,
|
||||
icon: req.body.icon,
|
||||
created_at: moment().format('YYYY-MM-DD HH:MM:SS'),
|
||||
title_ids: req.body.title_ids,
|
||||
title_id: req.body.title_ids,
|
||||
community_id: snowflake.nextId(),
|
||||
is_recommended: req.body.is_recommended,
|
||||
name: req.body.name,
|
||||
browser_icon: `data:image/png;base64,${req.files.browserIcon[0].buffer.toString('base64')}`,
|
||||
CTR_browser_header: `data:image/png;base64,${req.files.CTRbrowserHeader[0].buffer.toString('base64')}`,
|
||||
WiiU_browser_header: `data:image/png;base64,${req.files.WiiUbrowserHeader[0].buffer.toString('base64')}`,
|
||||
description: req.body.description,
|
||||
};
|
||||
const newCommunity = new COMMUNITY(document);
|
||||
newCommunity.save();
|
||||
res.sendStatus(200);
|
||||
}
|
||||
else
|
||||
throw new Error('Invalid account ID or password');
|
||||
|
||||
}).catch(error =>
|
||||
{
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
error_code: 400,
|
||||
message: error.message
|
||||
};
|
||||
res.send(response);
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/discovery/update', upload.none(), function (req, res) {
|
||||
database.connect().then(async e => {
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
throw new Error('No service token supplied');
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
|
||||
if(pid === null)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
let user = await database.getUserByPID(pid);
|
||||
|
||||
if(user !== null)
|
||||
{
|
||||
if(config.authorized_PNIDs.indexOf(user.pid) === -1)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
let endpoint = await database.getServerConfig();
|
||||
endpoint.has_error = req.body.has_error;
|
||||
endpoint.save();
|
||||
res.send(endpoint);
|
||||
}
|
||||
else
|
||||
throw new Error('Invalid account ID or password');
|
||||
|
||||
}).catch(error =>
|
||||
{
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
error_code: 400,
|
||||
message: error.message
|
||||
};
|
||||
res.send(response);
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/users/all', function (req, res) {
|
||||
database.connect().then(async e => {
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
throw new Error('No service token supplied');
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
|
||||
if(pid === null)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
let user = await database.getUserByPID(pid);
|
||||
|
||||
if(user !== null)
|
||||
{
|
||||
if(config.authorized_PNIDs.indexOf(user.pid) === -1)
|
||||
throw new Error('Invalid credentials supplied');
|
||||
|
||||
res.send(await database.getUsers(-1))
|
||||
}
|
||||
else
|
||||
throw new Error('Invalid account ID or password');
|
||||
|
||||
}).catch(error =>
|
||||
{
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
error_code: 400,
|
||||
message: error.message
|
||||
};
|
||||
res.send(response);
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
421
src/services/juxt-web/routes/admin/home.js
Normal file
421
src/services/juxt-web/routes/admin/home.js
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
var express = require('express');
|
||||
var xml = require('object-to-xml');
|
||||
const database = require('../../../../database');
|
||||
const util = require('../../../../authentication');
|
||||
const config = require('../../../../config.json');
|
||||
const request = require("request");
|
||||
const ejs = require('ejs');
|
||||
var multer = require('multer');
|
||||
var upload = multer({ dest: 'uploads/' });
|
||||
var router = express.Router();
|
||||
|
||||
var cookieParser = require('cookie-parser');
|
||||
router.use(cookieParser());
|
||||
|
||||
router.get('/', upload.none(), function (req, res) {
|
||||
|
||||
database.connect().then(async e => {
|
||||
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
//console.log(req.headers["x-nintendo-servicetoken"]);
|
||||
if(pid === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
let user = await database.getUserByPID(pid);
|
||||
res.render('admin_home.ejs', {
|
||||
user: user,
|
||||
});
|
||||
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 15,
|
||||
message: "SERVER_ERROR"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.get('/discovery', upload.none(), function (req, res) {
|
||||
|
||||
database.connect().then(async e => {
|
||||
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
//console.log(req.headers["x-nintendo-servicetoken"]);
|
||||
if(pid === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
let user = await database.getUserByPID(pid);
|
||||
res.render('admin_discovery.ejs', {
|
||||
user: user,
|
||||
});
|
||||
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 15,
|
||||
message: "SERVER_ERROR"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.get('/communities', upload.none(), function (req, res) {
|
||||
|
||||
database.connect().then(async e => {
|
||||
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
//console.log(req.headers["x-nintendo-servicetoken"]);
|
||||
if(pid === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
let user = await database.getUserByPID(pid);
|
||||
res.render('admin_communities.ejs', {
|
||||
user: user,
|
||||
});
|
||||
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 15,
|
||||
message: "SERVER_ERROR"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.get('/posts', upload.none(), function (req, res) {
|
||||
|
||||
database.connect().then(async e => {
|
||||
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
//console.log(req.headers["x-nintendo-servicetoken"]);
|
||||
if(pid === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
let user = await database.getUserByPID(pid);
|
||||
res.render('admin_posts.ejs', {
|
||||
user: user,
|
||||
});
|
||||
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 15,
|
||||
message: "SERVER_ERROR"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.get('/communities/new', upload.none(), function (req, res) {
|
||||
|
||||
database.connect().then(async e => {
|
||||
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
//console.log(req.headers["x-nintendo-servicetoken"]);
|
||||
if(pid === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
let user = await database.getUserByPID(pid);
|
||||
res.render('admin_new_community.ejs', {
|
||||
user: user,
|
||||
});
|
||||
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 15,
|
||||
message: "SERVER_ERROR"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.get('/communities/:communityID', upload.none(), function (req, res) {
|
||||
|
||||
database.connect().then(async e => {
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
//console.log(req.headers["x-nintendo-servicetoken"]);
|
||||
if(pid === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
let user = await database.getUserByPID(pid);
|
||||
let community = await database.getCommunityByID(req.params.communityID);
|
||||
res.render('admin_edit_community.ejs', {
|
||||
user: user,
|
||||
community: community,
|
||||
});
|
||||
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 15,
|
||||
message: "SERVER_ERROR"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.get('/users', upload.none(), function (req, res) {
|
||||
|
||||
database.connect().then(async e => {
|
||||
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
//console.log(req.headers["x-nintendo-servicetoken"]);
|
||||
if(pid === null)
|
||||
{
|
||||
res.redirect('/login');
|
||||
return;
|
||||
}
|
||||
let user = await database.getUserByPID(pid);
|
||||
res.render('admin_users.ejs', {
|
||||
user: user,
|
||||
});
|
||||
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 15,
|
||||
message: "SERVER_ERROR"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.get('/login', upload.none(), function (req, res) {
|
||||
|
||||
database.connect().then(async e => {
|
||||
|
||||
//let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
if(req.cookies.token === null)
|
||||
{
|
||||
res.render('admin_login.ejs', {});
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = util.data.processServiceToken(req.cookies.token);
|
||||
//console.log(req.headers["x-nintendo-servicetoken"]);
|
||||
if(pid === null)
|
||||
{
|
||||
res.render('admin_login.ejs', {});
|
||||
return;
|
||||
}
|
||||
let user = await database.getUserByPID(pid);
|
||||
res.redirect('/');
|
||||
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 15,
|
||||
message: "SERVER_ERROR"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.get('/token', upload.none(), function (req, res) {
|
||||
request.get({
|
||||
url: "http://" + config.account_server_domain + "/v1/api/provider/service_token/@me",
|
||||
headers: {
|
||||
'X-Nintendo-Client-ID': config["X-Nintendo-Client-ID"],
|
||||
'X-Nintendo-Client-Secret': config["X-Nintendo-Client-Secret"],
|
||||
'X-Nintendo-Title-ID': req.headers['x-nintendo-title-id'],
|
||||
'authorization': req.headers['authorization'],
|
||||
}
|
||||
}, function (error, response, body) {
|
||||
if (!error && response.statusCode === 200) {
|
||||
res.send(body);
|
||||
}
|
||||
else
|
||||
{
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
error_code: 400,
|
||||
message: 'Invalid account ID or password'
|
||||
};
|
||||
res.send(response);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/login', upload.none(), function (req, res) {
|
||||
database.connect().then(async e => {
|
||||
let user_id = req.body.user_id;
|
||||
let user = await database.getUserByUsername(user_id);
|
||||
let password = req.body.password;
|
||||
if(user !== null && password !== null)
|
||||
{
|
||||
if(config.authorized_PNIDs.indexOf(user.pid) === -1)
|
||||
throw new Error('User is not authorized to access the application');
|
||||
let password_hash = await util.data.nintendoPasswordHash(password, user.pid);
|
||||
await request.post({
|
||||
url: "http://" + config.account_server_domain + "/v1/api/oauth20/access_token/generate",
|
||||
headers: {
|
||||
'X-Nintendo-Client-ID': config["X-Nintendo-Client-ID"],
|
||||
'X-Nintendo-Client-Secret': config["X-Nintendo-Client-Secret"],
|
||||
'X-Nintendo-Title-ID': '0005001010040100'
|
||||
},
|
||||
form: {
|
||||
user_id: user_id,
|
||||
password_type: 'hash',
|
||||
password: password_hash,
|
||||
grant_type: 'password'
|
||||
}
|
||||
}, function (error, response, body) {
|
||||
if (!error && response.statusCode === 200) {
|
||||
res.send(body);
|
||||
}
|
||||
else
|
||||
{
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
error_code: 400,
|
||||
message: 'Invalid account ID or password'
|
||||
};
|
||||
res.send(response);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
else
|
||||
throw new Error('Invalid account ID or password');
|
||||
|
||||
}).catch(error =>
|
||||
{
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
error_code: 400,
|
||||
message: error.message
|
||||
};
|
||||
res.send(response);
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -8,4 +8,6 @@ module.exports = {
|
|||
CTR_WEB: require('./ctr/web'),
|
||||
CTR_COMMUNITIES: require('./ctr/communities'),
|
||||
CTR_USER: require('./ctr/userpage'),
|
||||
WEB_ADMIN: require('./admin/home'),
|
||||
WEB_API: require('./admin/api'),
|
||||
};
|
||||
225
src/views/admin_communities.ejs
Normal file
225
src/views/admin_communities.ejs
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Juxt Admin Panel</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Create a column layout with Flexbox */
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Left column (menu) */
|
||||
.left {
|
||||
flex: 35%;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.left h2 {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* Right column (page content) */
|
||||
.right {
|
||||
flex: 65%;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/* Style the search box */
|
||||
#mySearch {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
padding: 11px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/* Style the navigation menu inside the left column */
|
||||
#myMenu {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#myMenu li a {
|
||||
padding: 12px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
display: block
|
||||
}
|
||||
|
||||
#myMenu li a:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2 style="display: inline-block">Juxt Admin Panel - <%= user.user_id %></h2> <img style="width: 57px; display: inline-block; position: absolute; right: 8px;" src="<%= user.pfp_uri %>">
|
||||
|
||||
<div class="row">
|
||||
<div class="left" style="background-color:#bbb;max-width: 10%;">
|
||||
<ul id="myMenu">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/communities">Communities</a></li>
|
||||
<li><a href="/posts">Posts</a></li>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/discovery">Discovery</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="right" style="background-color:#ddd;">
|
||||
<h2>Communities</h2>
|
||||
<button onclick="location.assign('/communities/new')">New</button>
|
||||
<input type="text" id="mySearch" onkeyup="myFunction()" placeholder="Search..">
|
||||
<div id="txtHint">Communities should be listed here. If not, that fucking blows</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
/**
|
||||
* Fetches table from MongoDB and
|
||||
* displays in on the main page
|
||||
*/
|
||||
function generate_table() {
|
||||
var xhttp;
|
||||
xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState === 4 && this.status === 200) {
|
||||
var data = JSON.parse(this.responseText);
|
||||
let header = '<style>\n' +
|
||||
' table {\n' +
|
||||
' font-family: arial, sans-serif;\n' +
|
||||
' border-collapse: collapse;\n' +
|
||||
' width: 60%%;\n' +
|
||||
' }\n' +
|
||||
'\n' +
|
||||
' th {\n' +
|
||||
' cursor: pointer;\n' +
|
||||
' }\n' +
|
||||
'\n' +
|
||||
' th, td {\n' +
|
||||
' text-align: left;\n' +
|
||||
' padding: 16px;\n' +
|
||||
' }\n' +
|
||||
'\n' +
|
||||
' tr {\n' +
|
||||
' border: black;\n' +
|
||||
' border-width: 2px;\n' +
|
||||
' border-style: groove;\n' +
|
||||
' }\n' +
|
||||
' </style>';
|
||||
let body;
|
||||
body = '<table id="adddrop"><tbody id="search-list"><tr>' +
|
||||
'<th>Icon</th>' +
|
||||
'<th onClick="sortTable(0)">Name</th>' +
|
||||
'<th onClick="sortTable(1)">Created At</th>' +
|
||||
'<th onClick="sortTable(1)">Title ID\'\s</th>' +
|
||||
'<th onClick="sortTable(2)">Followers</th>' +
|
||||
'</tr>';
|
||||
for(let i = 0; i < data.length; i++) {
|
||||
|
||||
body +=
|
||||
'<tr id="' + data[i].community_id + '" onclick="location.assign(\'/communities/\' + this.id)">' +
|
||||
'<td><img style="width: 80px " src="' + data[i].browser_icon + '"></img></td>' +
|
||||
'<td><a>' + data[i].name + '</a></td>' +
|
||||
'<td>' + data[i].created_at + '</td>' +
|
||||
'<td>' + data[i].title_ids + '</td>' +
|
||||
'<td>' + data[i].followers + '</td></tr>';
|
||||
}
|
||||
body += '</tbody></table>';
|
||||
document.getElementById("txtHint").innerHTML = header + '</head><body>' + body;
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/v1/communities/all", true);
|
||||
xhttp.send();
|
||||
}
|
||||
/**
|
||||
* Deletes entry from MongoDB
|
||||
* @param id
|
||||
*/
|
||||
function deleteScout(id) {
|
||||
var xhttp;
|
||||
var e = document.getElementById("addDrop");
|
||||
var period = e.options[e.selectedIndex].value;
|
||||
xhttp = new XMLHttpRequest();
|
||||
xhttp.open("GET", "/remove?p=" + period + "&id=" + id, true);
|
||||
xhttp.send();
|
||||
removeRow((id + "_row"));
|
||||
}
|
||||
/**
|
||||
* Removes row from table
|
||||
* @param id
|
||||
*/
|
||||
function removeRow(id) {
|
||||
var row = document.getElementById(id);
|
||||
row.parentNode.removeChild(row);
|
||||
}
|
||||
/**
|
||||
* Sorts table by selected column
|
||||
* @param n
|
||||
*/
|
||||
function sortTable(n) {
|
||||
var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
|
||||
table = document.getElementById("adddrop");
|
||||
switching = true;
|
||||
dir = "asc";
|
||||
while (switching) {
|
||||
switching = false;
|
||||
rows = table.rows;
|
||||
for (i = 1; i < (rows.length - 1); i++) {
|
||||
shouldSwitch = false;
|
||||
x = rows[i].getElementsByTagName("TD")[n];
|
||||
y = rows[i + 1].getElementsByTagName("TD")[n];
|
||||
if (dir === "asc") {
|
||||
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
|
||||
shouldSwitch = true;
|
||||
break;
|
||||
}
|
||||
} else if (dir === "desc") {
|
||||
if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
|
||||
shouldSwitch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldSwitch) {
|
||||
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
||||
switching = true;
|
||||
switchcount ++;
|
||||
} else {
|
||||
if (switchcount === 0 && dir === "asc") {
|
||||
dir = "desc";
|
||||
switching = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
generate_table();
|
||||
</script>
|
||||
<script>
|
||||
function myFunction() {
|
||||
var input, filter, ul, tr, a, i;
|
||||
input = document.getElementById("mySearch");
|
||||
filter = input.value.toUpperCase();
|
||||
ul = document.getElementById("search-list");
|
||||
tr = ul.getElementsByTagName("tr");
|
||||
for (i = 1; i < tr.length; i++) {
|
||||
a = tr[i].getElementsByTagName("a")[0];
|
||||
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
|
||||
tr[i].style.display = "";
|
||||
} else {
|
||||
tr[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
99
src/views/admin_discovery.ejs
Normal file
99
src/views/admin_discovery.ejs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Juxt Admin Panel</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Create a column layout with Flexbox */
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Left column (menu) */
|
||||
.left {
|
||||
flex: 35%;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.left h2 {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* Right column (page content) */
|
||||
.right {
|
||||
flex: 65%;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/* Style the search box */
|
||||
#mySearch {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
padding: 11px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/* Style the navigation menu inside the left column */
|
||||
#myMenu {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#myMenu li a {
|
||||
padding: 12px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
display: block
|
||||
}
|
||||
|
||||
#myMenu li a:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2 style="display: inline-block">Juxt Admin Panel - <%= user.user_id %></h2> <img style="width: 57px; display: inline-block; position: absolute; right: 8px;" src="<%= user.pfp_uri %>">
|
||||
|
||||
<div class="row">
|
||||
<div class="left" style="background-color:#bbb;max-width: 10%;">
|
||||
<ul id="myMenu">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/communities">Communities</a></li>
|
||||
<li><a href="/posts">Posts</a></li>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/discovery">Discovery</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="right" style="background-color:#ddd;">
|
||||
<h2>Discovery Server Status</h2>
|
||||
<form action="/v1/discovery/update" enctype="multipart/form-data" target="formSubmitFrame" method="post">
|
||||
<select name="has_error">
|
||||
<option value="0" selected>OPEN</option>
|
||||
<option value="1">SYSTEM_UPDATE_REQUIRED</option>
|
||||
<option value="2">SETUP_NOT_COMPLETE</option>
|
||||
<option value="3">SERVICE_MAINTENANCE</option>
|
||||
<option value="4">SERVICE_CLOSED</option>
|
||||
<option value="5">PARENTAL_CONTROLS_ENABLED</option>
|
||||
<option value="6">POSTING_LIMITED_PARENTAL_CONTROLS</option>
|
||||
<option value="7">PNID_BANNED</option>
|
||||
<option value="8">SERVER_ERROR</option>
|
||||
</select>
|
||||
<input type="submit" value="Submit"><br>
|
||||
<iframe name="formSubmitFrame"></iframe>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
138
src/views/admin_edit_community.ejs
Normal file
138
src/views/admin_edit_community.ejs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Juxt Admin Panel</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Create a column layout with Flexbox */
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Left column (menu) */
|
||||
.left {
|
||||
flex: 35%;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.left h2 {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* Right column (page content) */
|
||||
.right {
|
||||
flex: 65%;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/* Style the search box */
|
||||
#mySearch {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
padding: 11px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/* Style the navigation menu inside the left column */
|
||||
#myMenu {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#myMenu li a {
|
||||
padding: 12px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
display: block
|
||||
}
|
||||
|
||||
#myMenu li a:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2 style="display: inline-block">Juxt Admin Panel - <%= user.user_id %></h2> <img style="width: 57px; display: inline-block; position: absolute; right: 8px;" src="<%= user.pfp_uri %>">
|
||||
|
||||
<div class="row">
|
||||
<div class="left" style="background-color:#bbb;max-width: 10%;">
|
||||
<ul id="myMenu">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/communities">Communities</a></li>
|
||||
<li><a href="/posts">Posts</a></li>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/discovery">Discovery</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="right" style="background-color:#ddd;">
|
||||
<h2>Update <%=community.name%></h2>
|
||||
<form action="/v1/communities/<%= community.community_id %>/update" enctype="multipart/form-data" target="formSubmitFrame" method="post">
|
||||
<label for="name">Community Name:</label><br>
|
||||
<input type="text" id="name" name="name" value="<%=community.name%>"><br>
|
||||
|
||||
<label for="description">Description:</label><br>
|
||||
<input type="text" id="description" name="description" value="<%=community.description%>"><br>
|
||||
|
||||
<label for="title_ids">Title ID 1:</label><br>
|
||||
<input type="text" id="title_ids" name="title_ids[]" value="<%=community.title_ids[0]%>"><br>
|
||||
|
||||
<label for="title_ids">Title ID 2:</label><br>
|
||||
<input type="text" id="title_ids" name="title_ids[]" value="<%=community.title_ids[1]%>"><br>
|
||||
|
||||
<label for="title_ids">Title ID 3:</label><br>
|
||||
<input type="text" id="title_ids" name="title_ids[]" value="<%=community.title_ids[2]%>"><br>
|
||||
|
||||
<label for="icon">System Icon (B64 TGA):</label><br>
|
||||
<input type="text" id="icon" name="icon" value="<%=community.icon%>"><br><br>
|
||||
|
||||
Browser Icon (512px x 512px)<br>
|
||||
<input type="file" id="browserIcon" accept="image/png" name="browserIcon"><br>
|
||||
<img src="<%=community.browser_icon%>" height="150px"><br>
|
||||
|
||||
3DS Browser Banner<br>
|
||||
<input type="file" id="CTRbrowserHeader" accept="image/png" name="CTRbrowserHeader"><br>
|
||||
<img src="<%=community.CTR_browser_header%>" height="150px"><br>
|
||||
|
||||
Wii U Browser Banner (1498px x 328px)<br>
|
||||
<input type="file" id="WiiUbrowserHeader" accept="image/png" name="WiiUbrowserHeader"><br>
|
||||
<img src="<%=community.WiiU_browser_header%>" height="150px"><br>
|
||||
|
||||
Is Recommended?
|
||||
<input type="radio" id="isRecomended" name="is_recommended" value="1" <%if(community.is_recommended === 1) {%> checked <%}%>>
|
||||
<label for="isRecomended">True</label>
|
||||
<input type="radio" id="isNotRecomended" name="is_recommended" value="0" <%if(community.is_recommended === 0) {%> checked <%}%>>
|
||||
<label for="isNotRecomended">False</label><br>
|
||||
|
||||
Has Shop Page?
|
||||
<input type="radio" id="hasShopPage" name="has_shop_page" value="1" <%if(community.has_shop_page === 1) {%> checked <%}%>>
|
||||
<label for="hasShopPage">True</label>
|
||||
<input type="radio" id="noShopPage" name="has_shop_page" value="0" <%if(community.has_shop_page === 0) {%> checked <%}%>>
|
||||
<label for="noShopPage">False</label><br>
|
||||
|
||||
Platform
|
||||
<input type="radio" id="WiiU" name="platform_id" value="0" <%if(community.platform_id === 0) {%> checked <%}%>>
|
||||
<label for="platform_id">Wii U</label>
|
||||
<input type="radio" id="3DS" name="platform_id" value="1" <%if(community.platform_id === 1) {%> checked <%}%>>
|
||||
<label for="platform_id">3DS</label>
|
||||
<input type="radio" id="Both" name="platform_id" value="2" <%if(community.platform_id === 2) {%> checked <%}%>>
|
||||
<label for="platform_id">Both</label><br>
|
||||
|
||||
<input type="submit" value="Submit">
|
||||
<iframe name="formSubmitFrame"></iframe>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
86
src/views/admin_home.ejs
Normal file
86
src/views/admin_home.ejs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Juxt Admin Panel</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Create a column layout with Flexbox */
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Left column (menu) */
|
||||
.left {
|
||||
flex: 35%;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.left h2 {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* Right column (page content) */
|
||||
.right {
|
||||
flex: 65%;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/* Style the search box */
|
||||
#mySearch {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
padding: 11px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/* Style the navigation menu inside the left column */
|
||||
#myMenu {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#myMenu li a {
|
||||
padding: 12px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
display: block
|
||||
}
|
||||
|
||||
#myMenu li a:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2 style="display: inline-block">Juxt Admin Panel - <%= user.user_id %></h2> <img style="width: 57px; display: inline-block; position: absolute; right: 8px;" src="<%= user.pfp_uri %>">
|
||||
|
||||
<div class="row">
|
||||
<div class="left" style="background-color:#bbb;max-width: 10%;">
|
||||
<ul id="myMenu">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/communities">Communities</a></li>
|
||||
<li><a href="/posts">Posts</a></li>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/discovery">Discovery</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="right" style="background-color:#ddd;">
|
||||
<h2>Home</h2>
|
||||
<p>This is a temporary admin panel for Juxt that will likely be used throughout the beta, or at least until the main Pretendo Network Admin Panel is done lol.</p>
|
||||
<p>If you can see this and your not a developer. Please tell Jemma how on earth you pulled it off, cause we kinda need this website to be secure :p</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
177
src/views/admin_login.ejs
Normal file
177
src/views/admin_login.ejs
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Juxt Admin Login</title>
|
||||
<style>
|
||||
body {font-family: Arial, Helvetica, sans-serif;}
|
||||
|
||||
/* Full-width input fields */
|
||||
input[type=text], input[type=password] {
|
||||
width: 100%;
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Set a style for all buttons */
|
||||
button {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 14px 20px;
|
||||
margin: 8px 0;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* The Modal (background) */
|
||||
.modal {
|
||||
display: block; /* Hidden by default */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: auto; /* Enable scroll if needed */
|
||||
background-color: rgb(0,0,0); /* Fallback color */
|
||||
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
/* Modal Content/Box */
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 5% auto 15% auto; /* 5% from the top, 15% from the bottom and centered */
|
||||
border: 1px solid #888;
|
||||
width: 80%; /* Could be more or less, depending on screen size */
|
||||
}
|
||||
|
||||
/* Add Zoom Animation */
|
||||
.animate {
|
||||
-webkit-animation: animatezoom 0.6s;
|
||||
animation: animatezoom 0.6s;
|
||||
max-width: 35%;
|
||||
}
|
||||
|
||||
@-webkit-keyframes animatezoom {
|
||||
from {-webkit-transform: scale(0)}
|
||||
to {-webkit-transform: scale(1)}
|
||||
}
|
||||
|
||||
@keyframes animatezoom {
|
||||
from {transform: scale(0)}
|
||||
to {transform: scale(1)}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="id01" class="modal">
|
||||
|
||||
<form class="modal-content animate" id="login" method="post" onsubmit="submitForm(); return false">
|
||||
<div class="container">
|
||||
<label for="user_id"><b>Username</b></label>
|
||||
<input type="text" placeholder="Enter Username" name="user_id" autocomplete="username" required>
|
||||
|
||||
<label for="password"><b>Password</b></label>
|
||||
<input type="password" placeholder="Enter Password" name="password" autocomplete="current-password" required>
|
||||
|
||||
<button>Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", '/login');
|
||||
var user_id = document.getElementsByName('user_id')[0].value;
|
||||
var password = document.getElementsByName('password')[0].value;
|
||||
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if(xhr.status === 200)
|
||||
{
|
||||
setCookie('auth', xmlToJSON.parseString(xhr.responseText).OAuth20[0].access_token[0].token[0]._text)
|
||||
console.log(xmlToJSON.parseString(xhr.responseText).OAuth20[0].access_token[0].token[0]._text);
|
||||
setServiceToken();
|
||||
}
|
||||
else
|
||||
{
|
||||
alert(JSON.parse(xhr.response).message);
|
||||
}
|
||||
}};
|
||||
|
||||
var data = "user_id=" + user_id + "&password=" + password;
|
||||
|
||||
xhr.send(data);
|
||||
}
|
||||
function setServiceToken() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", '/token');
|
||||
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
xhr.setRequestHeader("X-Nintendo-Client-ID", "a2efa818a34fa16b8afbc8a74eba3eda");
|
||||
xhr.setRequestHeader("X-Nintendo-Client-Secret", "c91cdb5658bd4954ade78533a339cf9a");
|
||||
xhr.setRequestHeader("X-Nintendo-Title-ID", "000500301001610A");
|
||||
xhr.setRequestHeader("Authorization", "Bearer " + getCookie("auth"));
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if(xhr.status === 200)
|
||||
{
|
||||
setCookie('token', xmlToJSON.parseString(xhr.responseText).service_token[0].token[0]._text)
|
||||
console.log(xmlToJSON.parseString(xhr.responseText).service_token[0].token[0]._text);
|
||||
location.assign('/');
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log(xhr.response);
|
||||
}
|
||||
}};
|
||||
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function setCookie(cname, cvalue) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (60 * 60 * 1000));
|
||||
var expires = "expires="+ date.toUTCString();
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + date.toGMTString();
|
||||
}
|
||||
function getCookie(cname) {
|
||||
var name = cname + "=";
|
||||
var decodedCookie = decodeURIComponent(document.cookie);
|
||||
var ca = decodedCookie.split(';');
|
||||
for(var i = 0; i <ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var xmlToJSON = function () { this.version = "1.3.4"; var e = { mergeCDATA: !0, grokAttr: !0, grokText: !0, normalize: !0, xmlns: !0, namespaceKey: "_ns", textKey: "_text", valueKey: "_value", attrKey: "_attr", cdataKey: "_cdata", attrsAsObject: !0, stripAttrPrefix: !0, stripElemPrefix: !0, childrenAsArray: !0 }, t = new RegExp(/(?!xmlns)^.*:/), r = new RegExp(/^\s+|\s+$/g); return this.grokType = function (e) { return /^\s*$/.test(e) ? null : /^(?:true|false)$/i.test(e) ? "true" === e.toLowerCase() : isFinite(e) ? parseFloat(e) : e }, this.parseString = function (e, t) { return this.parseXML(this.stringToXML(e), t) }, this.parseXML = function (a, n) { for (var s in n) e[s] = n[s]; var l = {}, i = 0, o = ""; if (e.xmlns && a.namespaceURI && (l[e.namespaceKey] = a.namespaceURI), a.attributes && a.attributes.length > 0) { var c = {}; for (i; i < a.attributes.length; i++) { var u = a.attributes.item(i); m = {}; var p = ""; p = e.stripAttrPrefix ? u.name.replace(t, "") : u.name, e.grokAttr ? m[e.valueKey] = this.grokType(u.value.replace(r, "")) : m[e.valueKey] = u.value.replace(r, ""), e.xmlns && u.namespaceURI && (m[e.namespaceKey] = u.namespaceURI), e.attrsAsObject ? c[p] = m : l[e.attrKey + p] = m } e.attrsAsObject && (l[e.attrKey] = c) } if (a.hasChildNodes()) for (var y, d, m, h = 0; h < a.childNodes.length; h++)4 === (y = a.childNodes.item(h)).nodeType ? e.mergeCDATA ? o += y.nodeValue : l.hasOwnProperty(e.cdataKey) ? (l[e.cdataKey].constructor !== Array && (l[e.cdataKey] = [l[e.cdataKey]]), l[e.cdataKey].push(y.nodeValue)) : e.childrenAsArray ? (l[e.cdataKey] = [], l[e.cdataKey].push(y.nodeValue)) : l[e.cdataKey] = y.nodeValue : 3 === y.nodeType ? o += y.nodeValue : 1 === y.nodeType && (0 === i && (l = {}), d = e.stripElemPrefix ? y.nodeName.replace(t, "") : y.nodeName, m = xmlToJSON.parseXML(y), l.hasOwnProperty(d) ? (l[d].constructor !== Array && (l[d] = [l[d]]), l[d].push(m)) : (e.childrenAsArray ? (l[d] = [], l[d].push(m)) : l[d] = m, i++)); else o || (e.childrenAsArray ? (l[e.textKey] = [], l[e.textKey].push(null)) : l[e.textKey] = null); if (o) if (e.grokText) { var x = this.grokType(o.replace(r, "")); null !== x && void 0 !== x && (l[e.textKey] = x) } else e.normalize ? l[e.textKey] = o.replace(r, "").replace(/\s+/g, " ") : l[e.textKey] = o.replace(r, ""); return l }, this.xmlToString = function (e) { try { return e.xml ? e.xml : (new XMLSerializer).serializeToString(e) } catch (e) { return null } }, this.stringToXML = function (e) { try { var t = null; return window.DOMParser ? t = (new DOMParser).parseFromString(e, "text/xml") : (t = new ActiveXObject("Microsoft.XMLDOM"), t.async = !1, t.loadXML(e), t) } catch (e) { return null } }, this }.call({}); "undefined" != typeof module && null !== module && module.exports ? module.exports = xmlToJSON : "function" == typeof define && define.amd && define(function () { return xmlToJSON });
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
133
src/views/admin_new_community.ejs
Normal file
133
src/views/admin_new_community.ejs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Juxt Admin Panel</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Create a column layout with Flexbox */
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Left column (menu) */
|
||||
.left {
|
||||
flex: 35%;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.left h2 {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* Right column (page content) */
|
||||
.right {
|
||||
flex: 65%;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/* Style the search box */
|
||||
#mySearch {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
padding: 11px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/* Style the navigation menu inside the left column */
|
||||
#myMenu {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#myMenu li a {
|
||||
padding: 12px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
display: block
|
||||
}
|
||||
|
||||
#myMenu li a:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2 style="display: inline-block">Juxt Admin Panel - <%= user.user_id %></h2> <img style="width: 57px; display: inline-block; position: absolute; right: 8px;" src="<%= user.pfp_uri %>">
|
||||
|
||||
<div class="row">
|
||||
<div class="left" style="background-color:#bbb;max-width: 10%;">
|
||||
<ul id="myMenu">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/communities">Communities</a></li>
|
||||
<li><a href="/posts">Posts</a></li>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/discovery">Discovery</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="right" style="background-color:#ddd;">
|
||||
<h2>Create New Community</h2>
|
||||
<form action="/v1/communities/new" target="formSubmitFrame" enctype="multipart/form-data" method="post">
|
||||
<label for="name">Community Name:</label><br>
|
||||
<input type="text" id="name" name="name" required><br>
|
||||
|
||||
<label for="description">Description:</label><br>
|
||||
<input type="text" id="description" name="description" required><br>
|
||||
|
||||
<label for="title_ids">Title ID 1:</label><br>
|
||||
<input type="text" id="title_ids" name="title_ids[]" required><br>
|
||||
|
||||
<label for="title_ids">Title ID 2:</label><br>
|
||||
<input type="text" id="title_ids" name="title_ids[]"><br>
|
||||
|
||||
<label for="title_ids">Title ID 3:</label><br>
|
||||
<input type="text" id="title_ids" name="title_ids[]"><br>
|
||||
|
||||
<label for="icon">System Icon (B64 TGA):</label><br>
|
||||
<input type="text" id="icon" name="icon" required><br>
|
||||
|
||||
Browser Icon (512px x 512px)<br>
|
||||
<input type="file" id="browserIcon" accept="image/png" name="browserIcon" required><br>
|
||||
|
||||
3DS Browser Banner<br>
|
||||
<input type="file" id="CTRbrowserHeader" accept="image/png" name="CTRbrowserHeader" required><br>
|
||||
Wii U Browser Banner (1498px x 328px)<br>
|
||||
<input type="file" id="WiiUbrowserHeader" accept="image/png" name="WiiUbrowserHeader" required><br>
|
||||
Is Recommended?
|
||||
<input type="radio" id="isRecomended" name="is_recommended" value="1" required>
|
||||
<label for="isRecomended">True</label>
|
||||
<input type="radio" id="isNotRecomended" name="is_recommended" value="0" required>
|
||||
<label for="isNotRecomended">False</label><br>
|
||||
|
||||
Has Shop Page?
|
||||
<input type="radio" id="hasShopPage" name="has_shop_page" value="1" required>
|
||||
<label for="hasShopPage">True</label>
|
||||
<input type="radio" id="noShopPage" name="has_shop_page" value="0" required>
|
||||
<label for="noShopPage">False</label><br>
|
||||
|
||||
Platform
|
||||
<input type="radio" id="WiiU" name="platform_ID" value="0" required>
|
||||
<label for="platform_ID">Wii U</label>
|
||||
<input type="radio" id="3DS" name="platform_ID" value="1" required>
|
||||
<label for="platform_ID">3DS</label>
|
||||
<input type="radio" id="Both" name="platform_ID" value="2" required>
|
||||
<label for="platform_ID">Both</label><br>
|
||||
|
||||
<input type="submit" value="Submit">
|
||||
<iframe name="formSubmitFrame"></iframe>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
84
src/views/admin_posts.ejs
Normal file
84
src/views/admin_posts.ejs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Juxt Admin Panel</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Create a column layout with Flexbox */
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Left column (menu) */
|
||||
.left {
|
||||
flex: 35%;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.left h2 {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* Right column (page content) */
|
||||
.right {
|
||||
flex: 65%;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/* Style the search box */
|
||||
#mySearch {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
padding: 11px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/* Style the navigation menu inside the left column */
|
||||
#myMenu {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#myMenu li a {
|
||||
padding: 12px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
display: block
|
||||
}
|
||||
|
||||
#myMenu li a:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2 style="display: inline-block">Juxt Admin Panel - <%= user.user_id %></h2> <img style="width: 57px; display: inline-block; position: absolute; right: 8px;" src="<%= user.pfp_uri %>">
|
||||
|
||||
<div class="row">
|
||||
<div class="left" style="background-color:#bbb;max-width: 10%;">
|
||||
<ul id="myMenu">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/communities">Communities</a></li>
|
||||
<li><a href="/posts">Posts</a></li>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/discovery">Discovery</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="right" style="background-color:#ddd;">
|
||||
<h2>Posts</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
224
src/views/admin_users.ejs
Normal file
224
src/views/admin_users.ejs
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Juxt Admin Panel</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Create a column layout with Flexbox */
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Left column (menu) */
|
||||
.left {
|
||||
flex: 35%;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.left h2 {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* Right column (page content) */
|
||||
.right {
|
||||
flex: 65%;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/* Style the search box */
|
||||
#mySearch {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
padding: 11px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/* Style the navigation menu inside the left column */
|
||||
#myMenu {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#myMenu li a {
|
||||
padding: 12px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
display: block
|
||||
}
|
||||
|
||||
#myMenu li a:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2 style="display: inline-block">Juxt Admin Panel - <%= user.user_id %></h2> <img style="width: 57px; display: inline-block; position: absolute; right: 8px;" src="<%= user.pfp_uri %>">
|
||||
|
||||
<div class="row">
|
||||
<div class="left" style="background-color:#bbb;max-width: 10%;">
|
||||
<ul id="myMenu">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/communities">Communities</a></li>
|
||||
<li><a href="/posts">Posts</a></li>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/discovery">Discovery</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="right" style="background-color:#ddd;">
|
||||
<h2>Communities</h2>
|
||||
<input type="text" id="mySearch" onkeyup="myFunction()" placeholder="Search..">
|
||||
<div id="txtHint">Communities should be listed here. If not, that fucking blows</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
/**
|
||||
* Fetches table from MongoDB and
|
||||
* displays in on the main page
|
||||
*/
|
||||
function generate_table() {
|
||||
var xhttp;
|
||||
xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState === 4 && this.status === 200) {
|
||||
var data = JSON.parse(this.responseText);
|
||||
let header = '<style>\n' +
|
||||
' table {\n' +
|
||||
' font-family: arial, sans-serif;\n' +
|
||||
' border-collapse: collapse;\n' +
|
||||
' width: 60%%;\n' +
|
||||
' }\n' +
|
||||
'\n' +
|
||||
' th {\n' +
|
||||
' cursor: pointer;\n' +
|
||||
' }\n' +
|
||||
'\n' +
|
||||
' th, td {\n' +
|
||||
' text-align: left;\n' +
|
||||
' padding: 16px;\n' +
|
||||
' }\n' +
|
||||
'\n' +
|
||||
' tr {\n' +
|
||||
' border: black;\n' +
|
||||
' border-width: 2px;\n' +
|
||||
' border-style: groove;\n' +
|
||||
' }\n' +
|
||||
' </style>';
|
||||
let body;
|
||||
body = '<table id="adddrop"><tbody id="search-list"><tr>' +
|
||||
'<th>Icon</th>' +
|
||||
'<th onClick="sortTable(0)">Name</th>' +
|
||||
'<th onClick="sortTable(1)">Created At</th>' +
|
||||
'<th onClick="sortTable(1)">Account Status</th>' +
|
||||
'<th onClick="sortTable(2)">Followers</th>' +
|
||||
'</tr>';
|
||||
for(let i = 0; i < data.length; i++) {
|
||||
|
||||
body +=
|
||||
'<tr id="' + data[i].pid + '" onclick="alert(this.id)">' +
|
||||
'<td><img style="width: 80px " src="' + data[i].pfp_uri + '"></img></td>' +
|
||||
'<td><a>' + data[i].user_id + '</a></td>' +
|
||||
'<td>' + data[i].created_at + '</td>' +
|
||||
'<td>' + data[i].account_status + '</td>' +
|
||||
'<td>' + data[i].followers + '</td></tr>';
|
||||
}
|
||||
body += '</tbody></table>';
|
||||
document.getElementById("txtHint").innerHTML = header + '</head><body>' + body;
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/v1/users/all", true);
|
||||
xhttp.send();
|
||||
}
|
||||
/**
|
||||
* Deletes entry from MongoDB
|
||||
* @param id
|
||||
*/
|
||||
function deleteScout(id) {
|
||||
var xhttp;
|
||||
var e = document.getElementById("addDrop");
|
||||
var period = e.options[e.selectedIndex].value;
|
||||
xhttp = new XMLHttpRequest();
|
||||
xhttp.open("GET", "/remove?p=" + period + "&id=" + id, true);
|
||||
xhttp.send();
|
||||
removeRow((id + "_row"));
|
||||
}
|
||||
/**
|
||||
* Removes row from table
|
||||
* @param id
|
||||
*/
|
||||
function removeRow(id) {
|
||||
var row = document.getElementById(id);
|
||||
row.parentNode.removeChild(row);
|
||||
}
|
||||
/**
|
||||
* Sorts table by selected column
|
||||
* @param n
|
||||
*/
|
||||
function sortTable(n) {
|
||||
var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
|
||||
table = document.getElementById("adddrop");
|
||||
switching = true;
|
||||
dir = "asc";
|
||||
while (switching) {
|
||||
switching = false;
|
||||
rows = table.rows;
|
||||
for (i = 1; i < (rows.length - 1); i++) {
|
||||
shouldSwitch = false;
|
||||
x = rows[i].getElementsByTagName("TD")[n];
|
||||
y = rows[i + 1].getElementsByTagName("TD")[n];
|
||||
if (dir === "asc") {
|
||||
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
|
||||
shouldSwitch = true;
|
||||
break;
|
||||
}
|
||||
} else if (dir === "desc") {
|
||||
if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
|
||||
shouldSwitch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldSwitch) {
|
||||
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
||||
switching = true;
|
||||
switchcount ++;
|
||||
} else {
|
||||
if (switchcount === 0 && dir === "asc") {
|
||||
dir = "desc";
|
||||
switching = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
generate_table();
|
||||
</script>
|
||||
<script>
|
||||
function myFunction() {
|
||||
var input, filter, ul, tr, a, i;
|
||||
input = document.getElementById("mySearch");
|
||||
filter = input.value.toUpperCase();
|
||||
ul = document.getElementById("search-list");
|
||||
tr = ul.getElementsByTagName("tr");
|
||||
for (i = 1; i < tr.length; i++) {
|
||||
a = tr[i].getElementsByTagName("a")[0];
|
||||
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
|
||||
tr[i].style.display = "";
|
||||
} else {
|
||||
tr[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user