integrate telemetry module #12
This commit is contained in:
@@ -2,13 +2,19 @@ class AccessManager {
|
|||||||
|
|
||||||
name = 'am'
|
name = 'am'
|
||||||
init(app){
|
init(app){
|
||||||
|
['user', 'editor', 'admin'].forEach(f => {
|
||||||
|
this[f] = this.oneOfThese([f]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
start(app){
|
start(app){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIp(req){
|
||||||
|
return req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
async audit(req, action, objectId, custom){
|
async audit(req, action, objectId, custom){
|
||||||
let data = {
|
let data = {
|
||||||
t: Math.floor(Date.now() / 1000),
|
t: Math.floor(Date.now() / 1000),
|
||||||
@@ -19,7 +25,7 @@ class AccessManager {
|
|||||||
l: req?.lang?.code,
|
l: req?.lang?.code,
|
||||||
u: req.user?._id && this.app.db.ObjectId(req.user._id),
|
u: req.user?._id && this.app.db.ObjectId(req.user._id),
|
||||||
a: action,
|
a: action,
|
||||||
o: objectId && this.app.db.ObjectId(objectId),
|
o: objectId,
|
||||||
c: custom
|
c: custom
|
||||||
}
|
}
|
||||||
await this.app.db.create('log', data);
|
await this.app.db.create('log', data);
|
||||||
@@ -128,6 +134,20 @@ class AccessManager {
|
|||||||
return user && user.roles.indexOf(role) > -1;
|
return user && user.roles.indexOf(role) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oneOfThese(roles){
|
||||||
|
return function(req, res, next){
|
||||||
|
let result = false;
|
||||||
|
roles.forEach(r=>{
|
||||||
|
if (req.user && (req.user.roles?.indexOf(r)>-1 || req.user.groups?.filter(g=>g.startsWith(r+'.')).length)
|
||||||
|
|| r == 'guest' ){
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (result) next();
|
||||||
|
else res.status(401).json({status:'error', error:'noPermissions'}).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AccessManager }
|
export { AccessManager }
|
||||||
@@ -123,6 +123,12 @@ class Config{
|
|||||||
* @memberof CookieOptions
|
* @memberof CookieOptions
|
||||||
*/
|
*/
|
||||||
maxAge: 1000 * 60 * 60 * 24 * 7
|
maxAge: 1000 * 60 * 60 * 24 * 7
|
||||||
|
},
|
||||||
|
sso:{
|
||||||
|
FACEBOOK_APP_ID:'your_fb_app_id',
|
||||||
|
FACEBOOK_APP_SECRET:'your_fb_app_secret',
|
||||||
|
GOOGLE_CLIENT_ID:'your_google_client_id',
|
||||||
|
GOOGLE_CLIENT_SECRET:'your_google_client_secret'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,250 @@
|
|||||||
|
import md5 from 'md5';
|
||||||
|
import passport from 'passport';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import { Strategy as LocalStrategy } from 'passport-local';
|
||||||
|
import { Strategy as FacebookStrategy } from 'passport-facebook';
|
||||||
|
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
|
||||||
|
|
||||||
|
const collection = 'users';
|
||||||
|
const emailRegexp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||||
|
|
||||||
|
|
||||||
|
class UserManager {
|
||||||
|
name = 'user';
|
||||||
|
/**
|
||||||
|
* Class initializer, инициализация на плъгин
|
||||||
|
* @param {App} app Class initializer, основна апликация
|
||||||
|
*/
|
||||||
|
init(app) {
|
||||||
|
const { db, config, am, global, utils, custom } = app;
|
||||||
|
|
||||||
|
this.passport = passport;
|
||||||
|
|
||||||
|
passport.serializeUser(function (user, done) {
|
||||||
|
done(null, user);
|
||||||
|
});
|
||||||
|
|
||||||
|
passport.deserializeUser(function (user, done) {
|
||||||
|
done(null, user);
|
||||||
|
});
|
||||||
|
|
||||||
|
passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' },
|
||||||
|
async function (email, password, done) {
|
||||||
|
let user = await db.get(collection, { email: { $regex: `^${global.JsUtils.escapeRegExp(email)}$`, $options: 'i' } });
|
||||||
|
if (user) {
|
||||||
|
if (md5(md5(password) + config.am.salt) == user.password) {
|
||||||
|
return done(null, am.getUserProfile(user));
|
||||||
|
} else {
|
||||||
|
await global.JsUtils.wait(3000);
|
||||||
|
return done(null, false, { message: 'invalidPassword' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await global.JsUtils.wait(3000);
|
||||||
|
return done(null, false, { message: 'invalidUsername' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
passport.use(new FacebookStrategy({
|
||||||
|
clientID: config.am.sso.FACEBOOK_APP_ID,
|
||||||
|
clientSecret: config.am.sso.FACEBOOK_APP_SECRET,
|
||||||
|
callbackURL: config.site.host + "/api/user/auth/facebook/callback",
|
||||||
|
profileFields: ['id', 'emails', 'name', 'photos']
|
||||||
|
}, am.processSocialLogin));
|
||||||
|
|
||||||
|
passport.use(new GoogleStrategy({
|
||||||
|
clientID: config.am.sso.GOOGLE_CLIENT_ID,
|
||||||
|
clientSecret: config.am.sso.GOOGLE_CLIENT_SECRET,
|
||||||
|
callbackURL: config.site.host + "/api/user/auth/google/callback",
|
||||||
|
userProfileURL: 'https://www.googleapis.com/oauth2/v3/userinfo'
|
||||||
|
}, am.processSocialLogin));
|
||||||
|
|
||||||
|
this.update = async function (ctx, data) {
|
||||||
|
if (!am.is(ctx.user, 'admin') && ctx.user._id != data._id) {
|
||||||
|
throw new Error('unauthorized');
|
||||||
|
}
|
||||||
|
let newData = data;
|
||||||
|
let user = await db.get(collection, { '_id': db.ObjectId(newData._id) });
|
||||||
|
|
||||||
|
if (newData.password) {
|
||||||
|
if (newData.password != newData.passConfirm) {
|
||||||
|
throw new Error('passwordMismatch')
|
||||||
|
}
|
||||||
|
if (!am.is(ctx.user, 'admin') && md5(md5(newData.passCurrent) + config.am.salt) != user.password) {
|
||||||
|
throw new Error('invalidPassword')
|
||||||
|
}
|
||||||
|
user.password = md5(md5(newData.password) + config.am.salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hist = Object.assign({}, user);
|
||||||
|
await am.addToHistory(hist, collection, 'update');
|
||||||
|
|
||||||
|
user._meta = user._meta || {};
|
||||||
|
am.setMeta(user._meta, 'update', ctx.user);
|
||||||
|
let safeData = {};
|
||||||
|
|
||||||
|
this.assignSafeUserData(ctx, safeData, newData, am.is(ctx.user, 'admin'));
|
||||||
|
this.assignSafeUserData(ctx, user, newData, am.is(ctx.user, 'admin'));
|
||||||
|
|
||||||
|
await this.updateUserSession(ctx, user._id, safeData);
|
||||||
|
|
||||||
|
am.audit(ctx, 'userUpdate', user._id);
|
||||||
|
|
||||||
|
await db.update(collection, { '_id': db.ObjectId(user._id) }, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.assignSafeUserData = function (ctx, userObject, newData, isAdmin) {
|
||||||
|
['displayName', 'firstName', 'lastName', ...(isAdmin ? ['roles', 'groups', 'email', 'status'] : [])].forEach(e => {
|
||||||
|
userObject[e] = newData[e];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateUserSession = async function (ctx, _uid, safeData) {
|
||||||
|
if (ctx.user?._id == _uid.toString()) {
|
||||||
|
Object.assign(ctx.session.passport.user, safeData);
|
||||||
|
}
|
||||||
|
if (am.is(ctx.user, 'admin') || ctx.allUserSessions) {
|
||||||
|
let userSessions = await db.list('user_sessions', {
|
||||||
|
query: {
|
||||||
|
'session.passport.user._id': _uid
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (let us of (userSessions?.data || [])) {
|
||||||
|
Object.assign(us.session.passport.user, safeData);
|
||||||
|
await db.update('user_sessions', { _id: us._id }, us);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delete = async function (ctx, uid) {
|
||||||
|
let user = await db.get(collection, { '_id': db.ObjectId(uid) });
|
||||||
|
let hist = Object.assign({}, user);
|
||||||
|
await am.addToHistory(hist, collection, 'remove');
|
||||||
|
am.audit(ctx, 'userDelete', user._id);
|
||||||
|
await db.remove(collection, { '_id': db.ObjectId(user._id) });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.signUp = async function (ctx, data) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
if (!emailRegexp.test(data.email)) {
|
||||||
|
return reject(new Error('invalidEmail'));
|
||||||
|
}
|
||||||
|
let exists = await db.get(collection, { email: { $regex: `^${global.JsUtils.escapeRegExp(data.email)}$`, $options: 'i' } });
|
||||||
|
if (exists) {
|
||||||
|
return reject(new Error('emailExists'))
|
||||||
|
}
|
||||||
|
if (!data.password || data.password != data.passConfirm) {
|
||||||
|
return reject(new Error('passwordMismatch'))
|
||||||
|
}
|
||||||
|
if (!ctx.session.captcha || ctx.session.captcha?.toLowerCase() != data.captcha?.toLowerCase()) {
|
||||||
|
return reject(new Error('invalidCaptcha'))
|
||||||
|
}
|
||||||
|
delete ctx.session.captcha;
|
||||||
|
let dbUser = {
|
||||||
|
email: data.email,
|
||||||
|
password: data.password && md5(md5(data.password) + config.am.salt),
|
||||||
|
roles: ['user'],
|
||||||
|
status: 0 //0 - neutral
|
||||||
|
};
|
||||||
|
if (config.am.validateEmail) {
|
||||||
|
dbUser.status = 9; // 9 - needs validation
|
||||||
|
dbUser.mailValidation = {
|
||||||
|
key: md5(md5(uuidv4() + data.email))
|
||||||
|
}
|
||||||
|
this.sendValidationEmail(ctx, dbUser).catch(error => {
|
||||||
|
am.audit(ctx, 'errorSendingMail', null, {
|
||||||
|
email: data.email,
|
||||||
|
error
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let r = await db.create(collection, dbUser);
|
||||||
|
dbUser._id = r.insertedId;
|
||||||
|
am.audit(ctx, 'signup', dbUser._id);
|
||||||
|
|
||||||
|
ctx.login(am.getUserProfile(dbUser), (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
reject(new Error('unknown'));
|
||||||
|
} else {
|
||||||
|
resolve(ctx.user);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.forgotten = async function (ctx, data) {
|
||||||
|
if (!ctx.session.captcha || ctx.session.captcha?.toLowerCase() != data.captcha?.toLowerCase()) {
|
||||||
|
throw new Error('invalidCaptcha')
|
||||||
|
}
|
||||||
|
delete ctx.session.captcha;
|
||||||
|
let exists = await db.get(collection, { email: data.email });
|
||||||
|
if (!exists) {
|
||||||
|
throw new Error('invalidEmail')
|
||||||
|
}
|
||||||
|
exists.pwdReset = {
|
||||||
|
key: md5(md5(uuidv4() + data.email)),
|
||||||
|
exp: (Date.now() / 1000) + 60 * 60 * 24
|
||||||
|
};
|
||||||
|
am.audit(ctx, 'userPwdForgotten', exists._id);
|
||||||
|
let l = ctx.lang.t;
|
||||||
|
|
||||||
|
await utils.sendMail({
|
||||||
|
from: `${config.apis.mailer.defaultMailFrom}`,
|
||||||
|
to: exists.email,
|
||||||
|
subject: `${l.siteName} - ${l['reset-password']}`,
|
||||||
|
html: l.forgottenPassMailContent(`${config.site.host}/${l._code}/user/change-password?key=${exists.pwdReset.key}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
await db.update(collection, { '_id': db.ObjectId(exists._id) }, exists);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reset = async function (ctx, data) {
|
||||||
|
let exists = await db.get(collection, { 'pwdReset.key': data.key });
|
||||||
|
if (!exists) {
|
||||||
|
throw new Error('invalidActivationLink')
|
||||||
|
}
|
||||||
|
if (exists.pwdReset.exp < Date.now / 1000) {
|
||||||
|
throw new Error('activationLinkExpired')
|
||||||
|
}
|
||||||
|
if (!data.password || data.password != data.passConfirm) {
|
||||||
|
throw new Error('passwordMismatch')
|
||||||
|
}
|
||||||
|
|
||||||
|
exists.password = data.password && md5(md5(data.password) + config.am.salt);
|
||||||
|
delete exists.pwdReset;
|
||||||
|
await db.update(collection, { '_id': db.ObjectId(exists._id) }, exists);
|
||||||
|
am.audit(ctx, 'userPwdReset', exists._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendValidationEmail = async function (ctx, data) {
|
||||||
|
let l = ctx.lang.t;
|
||||||
|
await utils.sendMail({
|
||||||
|
from: `${config.apis.mailer.defaultMailFrom}`,
|
||||||
|
to: data.email,
|
||||||
|
subject: `${l.siteName} - ${l['validate-email']}`,
|
||||||
|
html: l.validationMailContent(`${config.site.host}/${l._code}/user/validate-email?key=${data.mailValidation.key}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.validateEmail = async function (ctx, data) {
|
||||||
|
let exists = await db.get(collection, { 'mailValidation.key': data.key });
|
||||||
|
if (!exists) {
|
||||||
|
throw new Error('invalidValidationLink')
|
||||||
|
}
|
||||||
|
exists.status = 10;
|
||||||
|
delete exists.mailValidation;
|
||||||
|
custom.emailVerified?.(exists);
|
||||||
|
let safeData = {};
|
||||||
|
this.assignSafeUserData(ctx, safeData, exists, true);
|
||||||
|
let uid = exists._id;
|
||||||
|
await this.updateUserSession({ allUserSessions: true }, uid, safeData);
|
||||||
|
//db.update should be last, because it deletes exists._id, but I need it above!
|
||||||
|
await db.update(collection, { '_id': db.ObjectId(uid) }, exists);
|
||||||
|
am.audit(ctx, 'userMailValidated', uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { UserManager }
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import svgCaptcha from 'svg-captcha';
|
||||||
|
|
||||||
|
const collection = 'users';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UsersController. API for the user management, граничен клас за комуникация с потребителския модул
|
||||||
|
*/
|
||||||
|
class UsersController {
|
||||||
|
name = 'userApi'
|
||||||
|
route = '/api/user'
|
||||||
|
|
||||||
|
init(app) {
|
||||||
|
const { db, am, user, global } = app;
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/info', (req, res) => {
|
||||||
|
res.json({ user: req.user });
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/signin', function (req, res, next) {
|
||||||
|
user.passport.authenticate('local', function (err, user, info) {
|
||||||
|
if (err) { return next(err); }
|
||||||
|
if (!user) {
|
||||||
|
am.audit(req, 'login:error', null, { message: info.message });
|
||||||
|
return res.json({ status: 'error', message: info.message })
|
||||||
|
}
|
||||||
|
req.login(user, (err) => {
|
||||||
|
if (err) {
|
||||||
|
am.audit(req, 'login:error', null, { err });
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
res.json({ status: 'OK', user: req.user });
|
||||||
|
am.audit(req, 'login');
|
||||||
|
})
|
||||||
|
})(req, res, next);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/signup', async (req, res) => {
|
||||||
|
try {
|
||||||
|
await user.signUp(req, req.body);
|
||||||
|
res.json({ status: "OK", user: req.user });
|
||||||
|
} catch (err) {
|
||||||
|
res.json({ status: 'error', message: err.message, user: null });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/signout', async (req, res) => {
|
||||||
|
am.audit(req, 'logout');
|
||||||
|
req.logout(then => {
|
||||||
|
res.json({ status: 'OK' });
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/auth/facebook', user.passport.authenticate('facebook', { scope: ['email'] }));
|
||||||
|
router.get('/auth/facebook/callback', am.getSocialCallback('facebook'));
|
||||||
|
|
||||||
|
router.get('/auth/google', user.passport.authenticate('google', { scope: ['profile', 'email'] }));
|
||||||
|
router.get('/auth/google/callback', am.getSocialCallback('google'));
|
||||||
|
|
||||||
|
router.post('/tm', async (req, res) => {
|
||||||
|
am.audit(req, 'tm:' + req.body.action, req.body.object, req.body.data)
|
||||||
|
res.json({ status: "OK" });
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/update', am.user, async (req, res) => {
|
||||||
|
try {
|
||||||
|
await user.update(req, req.body);
|
||||||
|
res.json({ status: 'OK' });
|
||||||
|
} catch (err) {
|
||||||
|
if (err.message == 'unauthorized') {
|
||||||
|
res.status(401).json({ status: 'error', message: 'Unauthorized' })
|
||||||
|
} else {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ status: 'error' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/forgotten', async (req, res) => {
|
||||||
|
try {
|
||||||
|
await user.forgotten(req, req.body);
|
||||||
|
res.json({ status: "OK" });
|
||||||
|
} catch (err) {
|
||||||
|
res.json({ status: 'error', message: err.message });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/reset', async (req, res) => {
|
||||||
|
try {
|
||||||
|
await user.reset(req, req.body);
|
||||||
|
res.json({ status: "OK" });
|
||||||
|
} catch (err) {
|
||||||
|
res.json({ status: 'error', message: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/send-validation-email', async (req, res) => {
|
||||||
|
let dbUser = await db.get(collection, { '_id': db.ObjectId(req.body._id) });
|
||||||
|
if (dbUser.email != req.user?.email) {
|
||||||
|
res.json({ status: 'error', message: 'invalidEmail' });
|
||||||
|
}
|
||||||
|
if (dbUser) {
|
||||||
|
if (dbUser.status == 9) {
|
||||||
|
await user.sendValidationEmail(req, dbUser);
|
||||||
|
res.json({ status: "OK" });
|
||||||
|
} else if (dbUser.status == 10) {
|
||||||
|
res.json({ status: 'error', message: 'emailAlreadyValidated' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.json({ status: 'error', message: 'invalidEmail' });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/validate-email', async (req, res) => {
|
||||||
|
try {
|
||||||
|
await user.validateEmail(req, req.body);
|
||||||
|
res.json({ status: "OK" });
|
||||||
|
} catch (err) {
|
||||||
|
res.json({ status: 'error', message: err.message });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/get/:id', am.admin, async (req, res) => {
|
||||||
|
let user = await db.get(collection, { '_id': db.ObjectId(req.params.id) }, { password: 0 });
|
||||||
|
res.json(user);
|
||||||
|
})
|
||||||
|
|
||||||
|
router.delete('/delete/:id', am.admin, async (req, res) => {
|
||||||
|
await user.delete(req, req.params.id);
|
||||||
|
res.json({ status: 'OK' });
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/list', am.admin, async (req, res) => {
|
||||||
|
let q = {
|
||||||
|
query: {},
|
||||||
|
project: { password: 0 },
|
||||||
|
limit: req.body.limit || 12, skip: req.body.skip || 0
|
||||||
|
};
|
||||||
|
if (req.body.email) {
|
||||||
|
q.query.email = { $regex: global.JsUtils.escapeRegExp(req.body.email), $options: 'i' }
|
||||||
|
}
|
||||||
|
let list = await db.list(collection, q);
|
||||||
|
res.json(list);
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/captcha', (req, res) => {
|
||||||
|
let captcha = svgCaptcha.create({
|
||||||
|
noise: 2,
|
||||||
|
color: true
|
||||||
|
});
|
||||||
|
req.session.captcha = captcha.text;
|
||||||
|
res.type('svg');
|
||||||
|
res.status(200).send(captcha.data);
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/i-am-not-a-robot', async (req, res) => {
|
||||||
|
if (req.body.captcha?.toLowerCase() == req.session.captcha?.toLowerCase()) {
|
||||||
|
let cache = db.instance.collection('cache');
|
||||||
|
let queryKey = {
|
||||||
|
scope: 'ip', key: req.clientIP
|
||||||
|
}
|
||||||
|
let ipInfo = await cache.findOne(queryKey)
|
||||||
|
if (ipInfo) {
|
||||||
|
ipInfo.objects = [];
|
||||||
|
await cache.replaceOne(queryKey, ipInfo);
|
||||||
|
}
|
||||||
|
res.json({ status: 'OK' });
|
||||||
|
} else {
|
||||||
|
res.json({ status: 'error', message: 'invalidCaptcha' });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.webServer.xapp.use(this.route, router);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { UsersController }
|
||||||
@@ -17,6 +17,7 @@ const modules = [
|
|||||||
{name: 'GameObjectsManager', path:'app/bl/GameObjectsManager.js'},
|
{name: 'GameObjectsManager', path:'app/bl/GameObjectsManager.js'},
|
||||||
{name: 'ScenariosManager', path:'app/bl/ScenariosManager.js'},
|
{name: 'ScenariosManager', path:'app/bl/ScenariosManager.js'},
|
||||||
{name: 'GamesManager', path:'app/bl/GamesManager.js'},
|
{name: 'GamesManager', path:'app/bl/GamesManager.js'},
|
||||||
|
{name: 'UserManager', path:'app/bl/UserManager.js'},
|
||||||
|
|
||||||
{name: 'WebServer', path:'app/WebServer.js'},
|
{name: 'WebServer', path:'app/WebServer.js'},
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ const modules = [
|
|||||||
{name: 'GameObjectsController', path:'controllers/api/GameObjectsController.js'},
|
{name: 'GameObjectsController', path:'controllers/api/GameObjectsController.js'},
|
||||||
{name: 'ScenariosController', path:'controllers/api/ScenariosController.js'},
|
{name: 'ScenariosController', path:'controllers/api/ScenariosController.js'},
|
||||||
{name: 'GamesController', path:'controllers/api/GamesController.js'},
|
{name: 'GamesController', path:'controllers/api/GamesController.js'},
|
||||||
|
{name: 'UsersController', path:'controllers/api/UsersController.js'},
|
||||||
]
|
]
|
||||||
|
|
||||||
process.on('uncaughtException', err => {
|
process.on('uncaughtException', err => {
|
||||||
|
|||||||
Generated
+239
-4
@@ -21,11 +21,19 @@
|
|||||||
"express": "^4.21.1",
|
"express": "^4.21.1",
|
||||||
"express-session": "^1.18.1",
|
"express-session": "^1.18.1",
|
||||||
"helmet": "^8.0.0",
|
"helmet": "^8.0.0",
|
||||||
|
"md5": "^2.3.0",
|
||||||
"mongodb": "^6.10.0",
|
"mongodb": "^6.10.0",
|
||||||
|
"nodemailer": "^7.0.12",
|
||||||
|
"passport": "^0.7.0",
|
||||||
|
"passport-facebook": "^3.0.0",
|
||||||
|
"passport-google-oauth": "^2.0.0",
|
||||||
|
"passport-google-oauth20": "^2.0.0",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
"roboto-fontface": "*",
|
"roboto-fontface": "*",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
|
"svg-captcha": "^1.4.0",
|
||||||
"three-viewport-gizmo": "^2.2.0",
|
"three-viewport-gizmo": "^2.2.0",
|
||||||
"uuid": "^11.0.2",
|
"uuid": "^11.1.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vuetify": "^3.10.5"
|
"vuetify": "^3.10.5"
|
||||||
},
|
},
|
||||||
@@ -2452,6 +2460,15 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/base64url": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bidi-js": {
|
"node_modules/bidi-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||||
@@ -2737,6 +2754,15 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/charenc": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/check-error": {
|
"node_modules/check-error": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
|
||||||
@@ -3103,6 +3129,15 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/crypt": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cssesc": {
|
"node_modules/cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
@@ -5089,6 +5124,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-buffer": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/is-builtin-module": {
|
"node_modules/is-builtin-module": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
|
||||||
@@ -5548,6 +5589,17 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/md5": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"charenc": "0.0.2",
|
||||||
|
"crypt": "0.0.2",
|
||||||
|
"is-buffer": "~1.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/media-typer": {
|
"node_modules/media-typer": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
@@ -5827,6 +5879,15 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nodemailer": {
|
||||||
|
"version": "7.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.12.tgz",
|
||||||
|
"integrity": "sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA==",
|
||||||
|
"license": "MIT-0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
@@ -5850,6 +5911,12 @@
|
|||||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/oauth": {
|
||||||
|
"version": "0.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.2.tgz",
|
||||||
|
"integrity": "sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -5982,6 +6049,18 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/opentype.js": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.7.3.tgz",
|
||||||
|
"integrity": "sha512-Veui5vl2bLonFJ/SjX/WRWJT3SncgiZNnKUyahmXCc2sa1xXW15u3R/3TN5+JFiP7RsjK5ER4HA5eWaEmV9deA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tiny-inflate": "^1.0.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"ot": "bin/ot"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -6054,6 +6133,133 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/passport": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
|
||||||
|
"integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"passport-strategy": "1.x.x",
|
||||||
|
"pause": "0.0.1",
|
||||||
|
"utils-merge": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jaredhanson"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/passport-facebook": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-facebook/-/passport-facebook-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-K/qNzuFsFISYAyC1Nma4qgY/12V3RSLFdFVsPKXiKZt434wOvthFW1p7zKa1iQihQMRhaWorVE1o3Vi1o+ZgeQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"passport-oauth2": "1.x.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/passport-google-oauth": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-google-oauth/-/passport-google-oauth-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-JKxZpBx6wBQXX1/a1s7VmdBgwOugohH+IxCy84aPTZNq/iIPX6u7Mqov1zY7MKRz3niFPol0KJz8zPLBoHKtYA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"passport-google-oauth1": "1.x.x",
|
||||||
|
"passport-google-oauth20": "2.x.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/passport-google-oauth1": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-qpCEhuflJgYrdg5zZIpAq/K3gTqa1CtHjbubsEsidIdpBPLkEVq6tB1I8kBNcH89RdSiYbnKpCBXAZXX/dtx1Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"passport-oauth1": "1.x.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/passport-google-oauth20": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"passport-oauth2": "1.x.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/passport-local": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
|
||||||
|
"dependencies": {
|
||||||
|
"passport-strategy": "1.x.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/passport-oauth1": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-8T/nX4gwKTw0PjxP1xfD0QhrydQNakzeOpZ6M5Uqdgz9/a/Ag62RmJxnZQ4LkbdXGrRehQHIAHNAu11rCP46Sw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"oauth": "0.9.x",
|
||||||
|
"passport-strategy": "1.x.x",
|
||||||
|
"utils-merge": "1.x.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jaredhanson"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/passport-oauth1/node_modules/oauth": {
|
||||||
|
"version": "0.9.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||||
|
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/passport-oauth2": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64url": "3.x.x",
|
||||||
|
"oauth": "0.10.x",
|
||||||
|
"passport-strategy": "1.x.x",
|
||||||
|
"uid2": "0.0.x",
|
||||||
|
"utils-merge": "1.x.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jaredhanson"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/passport-strategy": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-exists": {
|
"node_modules/path-exists": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
@@ -6114,6 +6320,11 @@
|
|||||||
"node": ">= 14.16"
|
"node": ">= 14.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pause": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
||||||
|
},
|
||||||
"node_modules/pend": {
|
"node_modules/pend": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||||
@@ -7143,6 +7354,18 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svg-captcha": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg-captcha/-/svg-captcha-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-/fkkhavXPE57zRRCjNqAP3txRCSncpMx3NnNZL7iEoyAtYwUjPhJxW6FQTQPG5UPEmCrbFoXS10C3YdJlW7PDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"opentype.js": "^0.7.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tar-stream": {
|
"node_modules/tar-stream": {
|
||||||
"version": "1.6.2",
|
"version": "1.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
|
||||||
@@ -7189,6 +7412,12 @@
|
|||||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tinybench": {
|
"node_modules/tinybench": {
|
||||||
"version": "2.9.0",
|
"version": "2.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
||||||
@@ -7515,6 +7744,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/uid2": {
|
||||||
|
"version": "0.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
|
||||||
|
"integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/unbox-primitive": {
|
"node_modules/unbox-primitive": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
|
||||||
@@ -7843,9 +8078,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/uuid": {
|
"node_modules/uuid": {
|
||||||
"version": "11.0.2",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||||
"integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==",
|
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://github.com/sponsors/broofa",
|
"https://github.com/sponsors/broofa",
|
||||||
"https://github.com/sponsors/ctavan"
|
"https://github.com/sponsors/ctavan"
|
||||||
|
|||||||
+9
-1
@@ -23,11 +23,19 @@
|
|||||||
"express": "^4.21.1",
|
"express": "^4.21.1",
|
||||||
"express-session": "^1.18.1",
|
"express-session": "^1.18.1",
|
||||||
"helmet": "^8.0.0",
|
"helmet": "^8.0.0",
|
||||||
|
"md5": "^2.3.0",
|
||||||
"mongodb": "^6.10.0",
|
"mongodb": "^6.10.0",
|
||||||
|
"nodemailer": "^7.0.12",
|
||||||
|
"passport": "^0.7.0",
|
||||||
|
"passport-facebook": "^3.0.0",
|
||||||
|
"passport-google-oauth": "^2.0.0",
|
||||||
|
"passport-google-oauth20": "^2.0.0",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
"roboto-fontface": "*",
|
"roboto-fontface": "*",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
|
"svg-captcha": "^1.4.0",
|
||||||
"three-viewport-gizmo": "^2.2.0",
|
"three-viewport-gizmo": "^2.2.0",
|
||||||
"uuid": "^11.0.2",
|
"uuid": "^11.1.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vuetify": "^3.10.5"
|
"vuetify": "^3.10.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export default {
|
|||||||
async mounted(){
|
async mounted(){
|
||||||
if (this.id && this.id != 'add') {
|
if (this.id && this.id != 'add') {
|
||||||
this.object = (await this.$api.game.load(this.id)).data;
|
this.object = (await this.$api.game.load(this.id)).data;
|
||||||
|
//this.$api.user.tm('test', 'test', {data: 'test'})
|
||||||
}
|
}
|
||||||
this.scenarios = (await this.$api.scenario.search()).data.data;
|
this.scenarios = (await this.$api.scenario.search()).data.data;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ export default {
|
|||||||
async remove(id){
|
async remove(id){
|
||||||
return await $ax.delete(`/game/${id}`)
|
return await $ax.delete(`/game/${id}`)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
user:{
|
||||||
|
async tm(action, object, data){
|
||||||
|
return await $ax.post('/user/tm', {action, object, data});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user