En-/decryption fixed
This commit is contained in:
@@ -3,7 +3,7 @@ import path from 'path';
|
|||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import chatRouter from './routers/chatRouter.js';
|
import chatRouter from './routers/chatRouter.js';
|
||||||
import authRouter from './routers/authRouter.js';
|
import authRouter from './routers/authRouter.js';
|
||||||
import navigationRouter from './routers/navigationRouter.js'
|
import navigationRouter from './routers/navigationRouter.js';
|
||||||
import settingsRouter from './routers/settingsRouter.js';
|
import settingsRouter from './routers/settingsRouter.js';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
|
|
||||||
@@ -11,8 +11,15 @@ const __filename = fileURLToPath(import.meta.url);
|
|||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.use(cors());
|
const corsOptions = {
|
||||||
|
origin: '*',
|
||||||
|
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||||
|
preflightContinue: false,
|
||||||
|
optionsSuccessStatus: 204
|
||||||
|
};
|
||||||
|
|
||||||
|
app.use(cors(corsOptions));
|
||||||
app.use(express.json()); // To handle JSON request bodies
|
app.use(express.json()); // To handle JSON request bodies
|
||||||
|
|
||||||
app.use('/api/chat', chatRouter);
|
app.use('/api/chat', chatRouter);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export const register = async (req, res) => {
|
|||||||
const result = await userService.registerUser({ email, username, password, language });
|
const result = await userService.registerUser({ email, username, password, language });
|
||||||
res.status(201).json(result);
|
res.status(201).json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
res.status(500).json({ error: error.message });
|
res.status(500).json({ error: error.message });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,20 +8,23 @@ const menuStructure = {
|
|||||||
home: {
|
home: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
children: {},
|
children: {},
|
||||||
path: "/"
|
path: "/",
|
||||||
|
icon: "logo_mono.png"
|
||||||
},
|
},
|
||||||
friends: {
|
friends: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
children: {
|
children: {
|
||||||
manageFriends : {
|
manageFriends : {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
path: "/socialnetwork/friends"
|
path: "/socialnetwork/friends",
|
||||||
|
icon: "friends24.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showLoggedinFriends: 1
|
showLoggedinFriends: 1
|
||||||
},
|
},
|
||||||
socialnetwork: {
|
socialnetwork: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
|
icon: "socialnetwork.png",
|
||||||
children: {
|
children: {
|
||||||
guestbook: {
|
guestbook: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
@@ -56,6 +59,7 @@ const menuStructure = {
|
|||||||
},
|
},
|
||||||
chats: {
|
chats: {
|
||||||
visible: ["over12"],
|
visible: ["over12"],
|
||||||
|
icon: "chat.png",
|
||||||
children: {
|
children: {
|
||||||
multiChat: {
|
multiChat: {
|
||||||
visible: ["over12"],
|
visible: ["over12"],
|
||||||
@@ -69,6 +73,7 @@ const menuStructure = {
|
|||||||
},
|
},
|
||||||
falukant: {
|
falukant: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
|
icon: "falukant24.png",
|
||||||
children: {
|
children: {
|
||||||
create: {
|
create: {
|
||||||
visible: ["nofalukantaccount"],
|
visible: ["nofalukantaccount"],
|
||||||
@@ -130,9 +135,11 @@ const menuStructure = {
|
|||||||
},
|
},
|
||||||
minigames: {
|
minigames: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
|
icon: "minigames24.png",
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
|
icon: "settings24.png",
|
||||||
children: {
|
children: {
|
||||||
homepage: {
|
homepage: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
@@ -156,7 +163,7 @@ const menuStructure = {
|
|||||||
},
|
},
|
||||||
sexuality: {
|
sexuality: {
|
||||||
visible: ["over14"],
|
visible: ["over14"],
|
||||||
path: "/setting/sexuality"
|
path: "/settings/sexuality"
|
||||||
},
|
},
|
||||||
notifications: {
|
notifications: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
|
|||||||
@@ -3,10 +3,40 @@ import SettingsType from '../models/type/settings.js';
|
|||||||
import UserParam from '../models/community/user_param.js';
|
import UserParam from '../models/community/user_param.js';
|
||||||
import User from '../models/community/user.js';
|
import User from '../models/community/user.js';
|
||||||
import UserParamValue from '../models/type/user_param_value.js';
|
import UserParamValue from '../models/type/user_param_value.js';
|
||||||
|
import { calculateAge } from '../utils/userdata.js';
|
||||||
|
import { DataTypes, Op } from 'sequelize';
|
||||||
|
import { decrypt } from '../utils/encryption.js';
|
||||||
|
|
||||||
export const filterSettings = async (req, res) => {
|
export const filterSettings = async (req, res) => {
|
||||||
const { userid, type } = req.body;
|
const { userid, type } = req.body;
|
||||||
try {
|
try {
|
||||||
|
const user = await User.findOne({ where: { hashedId: userid } });
|
||||||
|
if (!user) {
|
||||||
|
return res.status(404).json({ error: 'User not found' });
|
||||||
|
}
|
||||||
|
const userParams = await UserParam.findAll({
|
||||||
|
where: { userId: user.id },
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: UserParamType,
|
||||||
|
as: 'paramType'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
let birthdate = null;
|
||||||
|
let gender = null;
|
||||||
|
|
||||||
|
for (const param of userParams) {
|
||||||
|
console.log(param.paramType.description);
|
||||||
|
if (param.paramType.description === 'birthdate') {
|
||||||
|
birthdate = param.value;
|
||||||
|
}
|
||||||
|
if (param.paramType.description === 'gender') {
|
||||||
|
const genderResult = await UserParamValue.findOne({ where: { id: param.value } });
|
||||||
|
gender = genderResult.dataValues.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const age = birthdate ? calculateAge(birthdate) : null;
|
||||||
const fields = await UserParamType.findAll({
|
const fields = await UserParamType.findAll({
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
@@ -26,9 +56,14 @@ export const filterSettings = async (req, res) => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
where: {
|
||||||
|
[Op.and]: [
|
||||||
|
{ minAge: { [Op.or]: [null, { [Op.lte]: age }] } },
|
||||||
|
{ gender: { [Op.or]: [null, gender] } }
|
||||||
|
]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const responseFields = await Promise.all(fields.map(async (field) => {
|
const responseFields = await Promise.all(fields.map(async (field) => {
|
||||||
const options = ['singleselect', 'multiselect'].includes(field.datatype) ? await UserParamValue.findAll({
|
const options = ['singleselect', 'multiselect'].includes(field.datatype) ? await UserParamValue.findAll({
|
||||||
where: { userParamTypeId: field.id }
|
where: { userParamTypeId: field.id }
|
||||||
@@ -44,11 +79,10 @@ export const filterSettings = async (req, res) => {
|
|||||||
options: options.map(opt => ({ id: opt.id, value: opt.value }))
|
options: options.map(opt => ({ id: opt.id, value: opt.value }))
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
res.status(200).json(responseFields);
|
res.status(200).json(responseFields);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching settings:', error);
|
console.error('Error filtering settings:', error);
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
res.status(500).json({ error: 'An error occurred while filtering the settings' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -108,4 +142,33 @@ export const getTypeParamValue = async(req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(200).json({ paramValueId: userParamValueObject.value });
|
res.status(200).json({ paramValueId: userParamValueObject.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getAccountSettings = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const user = await User.findOne({ where: { hashedId: req.body.userId } });
|
||||||
|
if (!user) {
|
||||||
|
return res.status(404).json({ error: 'User not found' });
|
||||||
|
}
|
||||||
|
const email = user.email;
|
||||||
|
res.status(200).json({ username: user.username, email, showinsearch: user.searchable });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error retrieving account settings:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setAccountSettings = async(req, res) => {
|
||||||
|
const { userid: userId, username, email, searchable, oldpassword, newpassword, newpasswordrepeat } = req.body;
|
||||||
|
const user = await User.findOne({ where: { hashedId: userId }});
|
||||||
|
if (!user) {
|
||||||
|
res.status(404).json({ error: 'User not found' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
user.searchable = searchable;
|
||||||
|
if (user.password !== oldpassword) {
|
||||||
|
res.status(401).json({error: 'Wrong password'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
backend/middleware/authMiddleware.js
Normal file
16
backend/middleware/authMiddleware.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import User from '../models/community/user.js';
|
||||||
|
|
||||||
|
export const authenticate = async (req, res, next) => {
|
||||||
|
const userId = req.headers.userid;
|
||||||
|
const authCode = req.headers.authcode;
|
||||||
|
if (!userId || !authCode) {
|
||||||
|
return res.status(401).json({ error: 'Unauthorized: Missing credentials' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.findOne({ where: { hashedId: userId, authCode } });
|
||||||
|
if (!user) {
|
||||||
|
return res.status(401).json({ error: 'Unauthorized: Invalid credentials' });
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: async (queryInterface, Sequelize) => {
|
||||||
|
await queryInterface.sequelize.transaction(async (transaction) => {
|
||||||
|
await queryInterface.addColumn(
|
||||||
|
'user',
|
||||||
|
'email_temp',
|
||||||
|
{
|
||||||
|
type: Sequelize.BLOB,
|
||||||
|
},
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryInterface.sequelize.query(
|
||||||
|
'UPDATE "community"."user" SET "email_temp" = "email"::bytea',
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryInterface.removeColumn('user', 'email', { transaction });
|
||||||
|
|
||||||
|
await queryInterface.renameColumn('user', 'email_temp', 'email', { transaction });
|
||||||
|
|
||||||
|
await queryInterface.changeColumn(
|
||||||
|
'user',
|
||||||
|
'email',
|
||||||
|
{
|
||||||
|
type: Sequelize.BLOB,
|
||||||
|
allowNull: false,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: async (queryInterface, Sequelize) => {
|
||||||
|
// Rollback code if needed
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,25 +1,18 @@
|
|||||||
import { sequelize } from '../../utils/sequelize.js';
|
import { sequelize } from '../../utils/sequelize.js';
|
||||||
import { DataTypes } from 'sequelize';
|
import { DataTypes } from 'sequelize';
|
||||||
import bcrypt from 'bcrypt';
|
import { encrypt, decrypt } from '../../utils/encryption.js';
|
||||||
import { encrypt, generateIv } from '../../utils/encryption.js';
|
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
|
||||||
const User = sequelize.define('user', {
|
const User = sequelize.define('user', {
|
||||||
email: {
|
email: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.BLOB, // Verwende BLOB, um die E-Mail als bytea zu speichern
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
const iv = generateIv();
|
this.setDataValue('email', Buffer.from(encrypt(value), 'hex'));
|
||||||
this.setDataValue('iv', iv.toString('hex'));
|
|
||||||
this.setDataValue('email', encrypt(value, iv));
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
|
||||||
iv: {
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
allowNull: false
|
|
||||||
},
|
},
|
||||||
username: {
|
username: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
@@ -46,6 +39,14 @@ const User = sequelize.define('user', {
|
|||||||
hashedId: {
|
hashedId: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true
|
allowNull: true
|
||||||
|
},
|
||||||
|
searchable: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
defaultValue: true
|
||||||
|
},
|
||||||
|
authCode: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'user',
|
tableName: 'user',
|
||||||
@@ -57,6 +58,11 @@ const User = sequelize.define('user', {
|
|||||||
user.hashedId = hashedId;
|
user.hashedId = hashedId;
|
||||||
await user.save();
|
await user.save();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
getterMethods: {
|
||||||
|
email() {
|
||||||
|
return decrypt(this.getDataValue('email').toString('hex'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -63,13 +63,14 @@ const UserParam = sequelize.define('user_param', {
|
|||||||
|
|
||||||
UserParam.upsertParam = async function (userId, paramTypeId, value) {
|
UserParam.upsertParam = async function (userId, paramTypeId, value) {
|
||||||
try {
|
try {
|
||||||
|
const val = value !== null && value !== undefined ? value.toString() : '';
|
||||||
const [userParam, created] = await UserParam.findOrCreate({
|
const [userParam, created] = await UserParam.findOrCreate({
|
||||||
where: { userId, paramTypeId },
|
where: { userId, paramTypeId },
|
||||||
defaults: { value }
|
defaults: { value: val }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!created) {
|
if (!created) {
|
||||||
userParam.value = value;
|
userParam.value = value !== null && value !== undefined ? value.toString() : '';
|
||||||
await userParam.save();
|
await userParam.save();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import { menu } from '../controllers/navigationController.js';
|
import { menu } from '../controllers/navigationController.js';
|
||||||
|
import { authenticate } from '../middleware/authMiddleware.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
router.use(authenticate);
|
||||||
|
|
||||||
router.get('/:userid', menu);
|
router.get('/:userid', menu);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import { filterSettings, updateSetting, getTypeParamValueId, getTypeParamValues, getTypeParamValue } from '../controllers/settingsController.js';
|
import { filterSettings, updateSetting, getTypeParamValueId, getTypeParamValues, getTypeParamValue, getAccountSettings } from '../controllers/settingsController.js';
|
||||||
|
import { authenticate } from '../middleware/authMiddleware.js';
|
||||||
|
|
||||||
const settingsRouter = Router();
|
const router = Router();
|
||||||
settingsRouter.post('/filter', filterSettings);
|
|
||||||
settingsRouter.post('/update', updateSetting);
|
|
||||||
settingsRouter.post('/getparamvalueid', getTypeParamValueId);
|
|
||||||
settingsRouter.post('/getparamvalues', getTypeParamValues);
|
|
||||||
settingsRouter.post('/getparamvalue/:id', getTypeParamValue);
|
|
||||||
|
|
||||||
export default settingsRouter;
|
router.post('/filter', authenticate, filterSettings);
|
||||||
|
router.post('/update', authenticate, updateSetting);
|
||||||
|
router.post('/account', authenticate, getAccountSettings);
|
||||||
|
|
||||||
|
router.post('/getparamvalues', getTypeParamValues);
|
||||||
|
router.post('/getparamvalueid', getTypeParamValueId);
|
||||||
|
router.post('/getparamvalue/:id', getTypeParamValue);
|
||||||
|
|
||||||
|
export default router;
|
||||||
|
|||||||
@@ -1,51 +1,53 @@
|
|||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
|
import crypto from 'crypto';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import User from '../models/community/user.js';
|
import User from '../models/community/user.js';
|
||||||
import UserParam from '../models/community/user_param.js';
|
import UserParam from '../models/community/user_param.js';
|
||||||
import UserParamType from '../models/type/user_param.js';
|
import UserParamType from '../models/type/user_param.js';
|
||||||
import { sendAccountActivationEmail, sendPasswordResetEmail } from './emailService.js';
|
import { sendAccountActivationEmail, sendPasswordResetEmail } from './emailService.js';
|
||||||
import { sequelize } from '../utils/sequelize.js';
|
import { sequelize } from '../utils/sequelize.js';
|
||||||
import { encrypt, generateIv } from '../utils/encryption.js';
|
|
||||||
|
|
||||||
const saltRounds = 10;
|
const saltRounds = 10;
|
||||||
|
|
||||||
export const registerUser = async ({ email, username, password, language }) => {
|
export const registerUser = async ({ email, username, password, language }) => {
|
||||||
const iv = generateIv();
|
const encryptionKey = process.env.SECRET_KEY;
|
||||||
const encryptedEmail = encrypt(email, iv);
|
|
||||||
const results = await sequelize.query(
|
const results = await sequelize.query(
|
||||||
`SELECT * FROM "community"."user" WHERE "email" = :email`,
|
`SELECT * FROM community.user WHERE pgp_sym_decrypt(email::bytea, :key) = :email`,
|
||||||
{
|
{
|
||||||
replacements: { key: process.env.SECRET_KEY, email: encryptedEmail },
|
replacements: { key: encryptionKey, email },
|
||||||
type: sequelize.QueryTypes.SELECT
|
type: sequelize.QueryTypes.SELECT
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (results.length && results.length > 0) {
|
if (results.length > 0) {
|
||||||
throw new Error('Email already in use');
|
throw new Error('emailinuse');
|
||||||
}
|
}
|
||||||
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
||||||
const resetToken = uuidv4();
|
const resetToken = uuidv4();
|
||||||
const user = await User.create({
|
const user = await User.create({
|
||||||
email: encryptedEmail,
|
email: email,
|
||||||
iv: iv.toString('hex'),
|
|
||||||
username,
|
username,
|
||||||
password: hashedPassword,
|
password: hashedPassword,
|
||||||
resetToken: resetToken,
|
resetToken: resetToken,
|
||||||
active: false,
|
active: false,
|
||||||
registration_date: new Date()
|
registration_date: new Date()
|
||||||
});
|
});
|
||||||
|
|
||||||
const languageType = await UserParamType.findOne({ where: { description: 'language' } });
|
const languageType = await UserParamType.findOne({ where: { description: 'language' } });
|
||||||
if (!languageType) {
|
if (!languageType) {
|
||||||
throw new Error('Language type not found');
|
throw new Error('languagenotfound');
|
||||||
}
|
}
|
||||||
await UserParam.create({
|
|
||||||
|
const languageParam = await UserParam.create({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
paramTypeId: languageType.id,
|
paramTypeId: languageType.id,
|
||||||
value: language
|
value: language
|
||||||
});
|
});
|
||||||
|
|
||||||
const activationLink = `${process.env.FRONTEND_URL}/activate?token=${resetToken}`;
|
const activationLink = `${process.env.FRONTEND_URL}/activate?token=${resetToken}`;
|
||||||
await sendAccountActivationEmail(email, activationLink, username, resetToken, language);
|
await sendAccountActivationEmail(email, activationLink, username, resetToken, language);
|
||||||
|
const authCode = crypto.randomBytes(20).toString('hex');
|
||||||
|
|
||||||
return { id: user.hashedId, username: user.username, active: user.active };
|
return { id: user.hashedId, username: user.username, active: user.active, param: [languageParam], authCode };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loginUser = async ({ username, password }) => {
|
export const loginUser = async ({ username, password }) => {
|
||||||
@@ -57,6 +59,9 @@ export const loginUser = async ({ username, password }) => {
|
|||||||
if (!match) {
|
if (!match) {
|
||||||
throw new Error('credentialsinvalid');
|
throw new Error('credentialsinvalid');
|
||||||
}
|
}
|
||||||
|
const authCode = crypto.randomBytes(20).toString('hex');
|
||||||
|
user.authCode = authCode;
|
||||||
|
await user.save();
|
||||||
const params = await UserParam.findAll({
|
const params = await UserParam.findAll({
|
||||||
where: {
|
where: {
|
||||||
userId: user.id
|
userId: user.id
|
||||||
@@ -69,9 +74,16 @@ export const loginUser = async ({ username, password }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const mappedParams = params.map(param => {
|
const mappedParams = params.map(param => {
|
||||||
return { 'name': param.paramType.description, 'value': param.value }; });
|
return { 'name': param.paramType.description, 'value': param.value };
|
||||||
return { id: user.hashedId, username: user.username, active: user.active, param: mappedParams };
|
});
|
||||||
|
return {
|
||||||
|
id: user.hashedId,
|
||||||
|
username: user.username,
|
||||||
|
active: user.active,
|
||||||
|
param: mappedParams,
|
||||||
|
authCode
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleForgotPassword = async ({ email }) => {
|
export const handleForgotPassword = async ({ email }) => {
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
|
||||||
const algorithm = 'aes-256-ctr';
|
const algorithm = 'aes-256-ecb'; // Verwende ECB-Modus, der keinen IV benötigt
|
||||||
const secretKey = process.env.SECRET_KEY;
|
const key = crypto.scryptSync(process.env.SECRET_KEY, 'salt', 32); // Der Schlüssel sollte eine Länge von 32 Bytes haben
|
||||||
|
|
||||||
export const encrypt = (text) => {
|
export const encrypt = (text) => {
|
||||||
const cipher = crypto.createCipheriv(algorithm, secretKey, Buffer.alloc(16, 0));
|
const cipher = crypto.createCipheriv(algorithm, key, null); // Kein IV verwendet
|
||||||
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
|
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||||
return encrypted.toString('hex');
|
encrypted += cipher.final('hex');
|
||||||
|
return encrypted;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const decrypt = (hash) => {
|
export const decrypt = (text) => {
|
||||||
const decipher = crypto.createDecipheriv(algorithm, secretKey, Buffer.alloc(16, 0));
|
const decipher = crypto.createDecipheriv(algorithm, key, null); // Kein IV verwendet
|
||||||
const decrpyted = Buffer.concat([decipher.update(Buffer.from(hash, 'hex')), decipher.final()]);
|
let decrypted = decipher.update(text, 'hex', 'utf8');
|
||||||
return decrpyted.toString();
|
decrypted += decipher.final('utf8');
|
||||||
|
return decrypted;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
|
||||||
const algorithm = 'aes-256-cbc';
|
const algorithm = 'aes-256-ecb';
|
||||||
const secretKey = process.env.SECRET_KEY;
|
const key = crypto.scryptSync(process.env.SECRET_KEY, 'salt', 32);
|
||||||
|
|
||||||
export const generateIv = () => {
|
export const generateIv = () => {
|
||||||
return crypto.randomBytes(16);
|
return crypto.randomBytes(16);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const encrypt = (text, iv) => {
|
export const encrypt = (text) => {
|
||||||
const cipher = crypto.createCipheriv(algorithm, Buffer.from(secretKey, 'utf-8'), iv);
|
const cipher = crypto.createCipheriv(algorithm, key, null);
|
||||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||||
encrypted += cipher.final('hex');
|
encrypted += cipher.final('hex');
|
||||||
return encrypted;
|
return encrypted;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const decrypt = (text, iv) => {
|
export const decrypt = (text) => {
|
||||||
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(secretKey, 'utf-8'), iv);
|
const decipher = crypto.createDecipheriv(algorithm, key, null);
|
||||||
let decrypted = decipher.update(text, 'hex', 'utf8');
|
let decrypted = decipher.update(text, 'hex', 'utf8');
|
||||||
decrypted += decipher.final('utf8');
|
decrypted += decipher.final('utf8');
|
||||||
return decrypted;
|
return decrypted;
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ const initializeTypes = async () => {
|
|||||||
sexualpreference: { type: 'singleselect', 'setting': 'sexuality', minAge: 14 },
|
sexualpreference: { type: 'singleselect', 'setting': 'sexuality', minAge: 14 },
|
||||||
gender: { type: 'singleselect', setting: 'personal' },
|
gender: { type: 'singleselect', setting: 'personal' },
|
||||||
pubichair: { type: 'singleselect', setting: 'sexuality', minAge: 14 },
|
pubichair: { type: 'singleselect', setting: 'sexuality', minAge: 14 },
|
||||||
penislenght: { type: 'int', setting: 'sexuality', minAge: 14, gender: 'm' },
|
penislenght: { type: 'int', setting: 'sexuality', minAge: 14, gender: 'male' },
|
||||||
brasize: { type: 'string', setting: 'sexuality', minAge: 14, gender: 'f' }
|
brasize: { type: 'string', setting: 'sexuality', minAge: 14, gender: 'female' }
|
||||||
};
|
};
|
||||||
Object.keys(userParams).forEach(async(key) => {
|
Object.keys(userParams).forEach(async(key) => {
|
||||||
const item = userParams[key];
|
const item = userParams[key];
|
||||||
@@ -56,6 +56,8 @@ const initializeTypes = async () => {
|
|||||||
hairlength: ['short', 'medium', 'long', 'bald', 'other'],
|
hairlength: ['short', 'medium', 'long', 'bald', 'other'],
|
||||||
skincolor: ['light', 'medium', 'dark', 'other'],
|
skincolor: ['light', 'medium', 'dark', 'other'],
|
||||||
freckles: ['much', 'medium', 'less', 'none'],
|
freckles: ['much', 'medium', 'less', 'none'],
|
||||||
|
sexualpreference: ['straight', 'gay', 'bi', 'pan', 'asexual'],
|
||||||
|
pubichair: ['none', 'short', 'medium', 'long', 'hairy', 'waxed', 'landingstrip', 'bikinizone', 'other'],
|
||||||
};
|
};
|
||||||
Object.keys(valuesList).forEach(async(key) => {
|
Object.keys(valuesList).forEach(async(key) => {
|
||||||
const values = valuesList[key];
|
const values = valuesList[key];
|
||||||
|
|||||||
10
backend/utils/userdata.js
Normal file
10
backend/utils/userdata.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export const calculateAge = (birthdate) => {
|
||||||
|
const today = new Date();
|
||||||
|
const birthDate = new Date(birthdate);
|
||||||
|
let age = today.getFullYear() - birthDate.getFullYear();
|
||||||
|
const monthDiff = today.getMonth() - birthDate.getMonth();
|
||||||
|
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
||||||
|
age--;
|
||||||
|
}
|
||||||
|
return age;
|
||||||
|
};
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(item, key) in menu" :key="key" class="mainmenuitem">
|
<li v-for="(item, key) in menu" :key="key" class="mainmenuitem" @click="openPage(item.path ?? null)">
|
||||||
<a :href="item.path" v-if="item.path" class="menuitem">{{ $t(`navigation.${key}`) }}</a>
|
<span v-if="item.icon" :style="`background-image:url('/images/icons/${item.icon}')`"
|
||||||
<span v-if="!item.path" class="menuitem">{{ $t(`navigation.${key}`) }}</span>
|
class="menu-icon"> </span>
|
||||||
|
<span>{{ $t(`navigation.${key}`) }}</span>
|
||||||
<ul v-if="item.children" class="submenu1">
|
<ul v-if="item.children" class="submenu1">
|
||||||
<li v-for="(subitem, subkey) in item.children" :key="subitem.text">
|
<li v-for="(subitem, subkey) in item.children" :key="subitem.text" @click="openPage(subitem.path ?? null)">
|
||||||
<a :href="subitem.path" v-if="subitem.path">{{ $t(`navigation.m-${key}.${subkey}`) }}</a>
|
<span v-if="subitem.icon" :style="`background-image:url('/images/icons/${subitem.icon}')`" class="submenu-icon"> </span>
|
||||||
<span v-if="!subitem.path">{{ $t(`navigation.m-${key}.${subkey}`) }}</span>
|
<span>{{ $t(`navigation.m-${key}.${subkey}`) }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -37,6 +38,11 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['loadMenu', 'logout']),
|
...mapActions(['loadMenu', 'logout']),
|
||||||
|
openPage(url) {
|
||||||
|
if (url) {
|
||||||
|
this.$router.push(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -71,11 +77,12 @@ nav>ul>li:hover {
|
|||||||
background-color: #D37C06;
|
background-color: #D37C06;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
nav>ul>li:hover > span {
|
|
||||||
|
nav>ul>li:hover>span {
|
||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav>ul>li:hover > ul{
|
nav>ul>li:hover>ul {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,13 +126,33 @@ a {
|
|||||||
left: 0;
|
left: 0;
|
||||||
top: 2.5em;
|
top: 2.5em;
|
||||||
}
|
}
|
||||||
.submenu1 > li {
|
|
||||||
|
.submenu1>li {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
color: #7E471B;
|
color: #7E471B;
|
||||||
}
|
}
|
||||||
|
|
||||||
.submenu1>li:hover {
|
.submenu1>li:hover {
|
||||||
color: #000000;
|
color: #000000;
|
||||||
background-color: #D37C06;
|
background-color: #D37C06;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 3px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
.submenu-icon {
|
||||||
|
width: 1.2em;
|
||||||
|
height: 1em;
|
||||||
|
margin-right: 3px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1em;
|
||||||
|
background-size: 1.2em 1.2em;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -13,15 +13,14 @@
|
|||||||
:list="getSettingOptions(setting.name, setting.options)" @input="handleInput(setting.id, $event)" />
|
:list="getSettingOptions(setting.name, setting.options)" @input="handleInput(setting.id, $event)" />
|
||||||
<InputNumberWidget v-else-if="setting.datatype == 'int'"
|
<InputNumberWidget v-else-if="setting.datatype == 'int'"
|
||||||
:labelTr="`settings.personal.label.${setting.name}`"
|
:labelTr="`settings.personal.label.${setting.name}`"
|
||||||
:tooltipTr="`settings.personal.tooltip.${setting.name}`" :value=setting.value min="0" max="200"
|
:tooltipTr="`settings.personal.tooltip.${setting.name}`" :value="convertToInt(setting.value)" min="0"
|
||||||
@input="handleInput(setting.id, $event)" />
|
max="200" @input="handleInput(setting.id, $event)" />
|
||||||
<FloatInputWidget v-else-if="setting.datatype == 'float'"
|
<FloatInputWidget v-else-if="setting.datatype == 'float'"
|
||||||
:labelTr="`settings.personal.label.${setting.name}`"
|
:labelTr="`settings.personal.label.${setting.name}`"
|
||||||
:tooltipTr="`settings.personal.tooltip.${setting.name}`" :value=setting.value
|
:tooltipTr="`settings.personal.tooltip.${setting.name}`" :value="convertToFloat(setting.value)"
|
||||||
@input="handleInput(setting.id, $event)" />
|
@input="handleInput(setting.id, $event)" />
|
||||||
<CheckboxWidget v-else-if="setting.datatype == 'bool'"
|
<CheckboxWidget v-else-if="setting.datatype == 'bool'" :labelTr="`settings.personal.label.${setting.name}`"
|
||||||
:labelTr="`settings.personal.label.${setting.name}`"
|
:tooltipTr="`settings.personal.tooltip.${setting.name}`" :value="convertToBool(setting.value)"
|
||||||
:tooltipTr="`settings.personal.tooltip.${setting.name}`" :value=setting.value
|
|
||||||
@input="handleInput(setting.id, $event)" />
|
@input="handleInput(setting.id, $event)" />
|
||||||
<div v-else>{{ setting }}
|
<div v-else>{{ setting }}
|
||||||
</div>
|
</div>
|
||||||
@@ -104,6 +103,23 @@ export default {
|
|||||||
{ value: 'en', captionTr: 'settings.personal.languages.en' },
|
{ value: 'en', captionTr: 'settings.personal.languages.en' },
|
||||||
{ value: 'de', captionTr: 'settings.personal.languages.de' },
|
{ value: 'de', captionTr: 'settings.personal.languages.de' },
|
||||||
];
|
];
|
||||||
|
},
|
||||||
|
convertToInt(value) {
|
||||||
|
const intValue = parseInt(value, 10);
|
||||||
|
return isNaN(intValue) ? 0 : intValue;
|
||||||
|
},
|
||||||
|
convertToFloat(value) {
|
||||||
|
const floatValue = parseFloat(value);
|
||||||
|
return isNaN(floatValue) ? 0.0 : floatValue;
|
||||||
|
},
|
||||||
|
convertToBool(value) {
|
||||||
|
if (value === 'true' || value === true) {
|
||||||
|
return true;
|
||||||
|
} else if (value === 'false' || value === false) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ label {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
formattedValue() {
|
formattedValue() {
|
||||||
return this.value != null ? this.value.toFixed(this.decimals) : '';
|
return this.value != null && typeof this.value === 'float' ? this.value.toFixed(this.decimals) : '';
|
||||||
},
|
},
|
||||||
step() {
|
step() {
|
||||||
return Math.pow(10, -this.decimals);
|
return Math.pow(10, -this.decimals);
|
||||||
|
|||||||
@@ -16,7 +16,11 @@
|
|||||||
"weight": "Gewicht",
|
"weight": "Gewicht",
|
||||||
"bodyheight": "Größe",
|
"bodyheight": "Größe",
|
||||||
"piercings": "Piercings",
|
"piercings": "Piercings",
|
||||||
"tattoos": "Tattoos"
|
"tattoos": "Tattoos",
|
||||||
|
"sexualpreference": "Ausrichtung",
|
||||||
|
"pubichair": "Schamhaare",
|
||||||
|
"penislenght": "Penislänge",
|
||||||
|
"brasize": "BH-Größe"
|
||||||
},
|
},
|
||||||
"tooltip": {
|
"tooltip": {
|
||||||
"language": "Sprache",
|
"language": "Sprache",
|
||||||
@@ -32,7 +36,11 @@
|
|||||||
"weight": "Gewicht",
|
"weight": "Gewicht",
|
||||||
"bodyheight": "Größe",
|
"bodyheight": "Größe",
|
||||||
"piercings": "Piercings",
|
"piercings": "Piercings",
|
||||||
"tattoos": "Tattoos"
|
"tattoos": "Tattoos",
|
||||||
|
"sexualpreference": "Ausrichtung",
|
||||||
|
"pubichair": "Schamhaare",
|
||||||
|
"penislenght": "Penislänge",
|
||||||
|
"brasize": "BH-Größe"
|
||||||
},
|
},
|
||||||
"gender": {
|
"gender": {
|
||||||
"male": "Männlich",
|
"male": "Männlich",
|
||||||
@@ -83,10 +91,42 @@
|
|||||||
"medium": "Mittel",
|
"medium": "Mittel",
|
||||||
"less": "Wenige",
|
"less": "Wenige",
|
||||||
"none": "Keine"
|
"none": "Keine"
|
||||||
|
},
|
||||||
|
"sexualpreference": {
|
||||||
|
"straight": "Heterosexuell",
|
||||||
|
"gay": "Homosexuell",
|
||||||
|
"bi": "Bisexuell",
|
||||||
|
"asexual": "Asexuell",
|
||||||
|
"pan": "Pansexuell"
|
||||||
|
},
|
||||||
|
"pubichair": {
|
||||||
|
"none": "Keine",
|
||||||
|
"short": "Kurz",
|
||||||
|
"medium": "Mittel",
|
||||||
|
"long": "Lang",
|
||||||
|
"hairy": "Natürlich",
|
||||||
|
"waxed": "Heißwachsentfernung",
|
||||||
|
"landingstrip": "Landebahn",
|
||||||
|
"bikinizone": "Nur Bikinizone",
|
||||||
|
"other": "Andere"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"view": {
|
"view": {
|
||||||
"title": "Aussehen"
|
"title": "Aussehen"
|
||||||
|
},
|
||||||
|
"sexuality": {
|
||||||
|
"title": "Sexualität"
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"title": "Account",
|
||||||
|
"username": "Benutzername",
|
||||||
|
"email": "Email-Adresse",
|
||||||
|
"newpassword": "Passwort",
|
||||||
|
"newpasswordretype": "Passwort wiederholen",
|
||||||
|
"deleteAccount": "Account löschen",
|
||||||
|
"language": "Sprache",
|
||||||
|
"showinsearch": "In Usersuchen anzeigen",
|
||||||
|
"changeaction": "Benutzerdaten ändern"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import HomeView from '../views/HomeView.vue';
|
|||||||
import ActivateView from '../views/auth/ActivateView.vue';
|
import ActivateView from '../views/auth/ActivateView.vue';
|
||||||
import PeronalSettingsView from '../views/settings/PersonalView.vue';
|
import PeronalSettingsView from '../views/settings/PersonalView.vue';
|
||||||
import ViewSettingsView from '../views/settings/ViewView.vue';
|
import ViewSettingsView from '../views/settings/ViewView.vue';
|
||||||
|
import SexualitySettingsView from '../views/settings/SexualityView.vue';
|
||||||
|
import AccountSettingsView from '../views/settings/AccountView.vue';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@@ -28,6 +30,18 @@ const routes = [
|
|||||||
component: ViewSettingsView,
|
component: ViewSettingsView,
|
||||||
meta: { requiresAuth: true }
|
meta: { requiresAuth: true }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/settings/sexuality',
|
||||||
|
name: 'Sexuality settings',
|
||||||
|
component: SexualitySettingsView,
|
||||||
|
meta: { requiresAuth: true }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/settings/account',
|
||||||
|
name: 'Account settings',
|
||||||
|
component: AccountSettingsView,
|
||||||
|
meta: { requiresAuth: true }
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import store from '../store';
|
||||||
|
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: process.env.VUE_APP_API_BASE_URL || 'http://localhost:3001',
|
baseURL: process.env.VUE_APP_API_BASE_URL || 'http://localhost:3001',
|
||||||
@@ -7,4 +8,18 @@ const apiClient = axios.create({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
apiClient.interceptors.request.use(config => {
|
||||||
|
console.log('loading user');
|
||||||
|
const user = store.getters.user;
|
||||||
|
console.log(user);
|
||||||
|
if (user && user.authCode) {
|
||||||
|
console.log('set auth');
|
||||||
|
config.headers['userId'] = user.id;
|
||||||
|
config.headers['authCode'] = user.authCode;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}, error => {
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
export default apiClient;
|
export default apiClient;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios';
|
import apiClient from './axios';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
|
|
||||||
const loadMenu = async () => {
|
const loadMenu = async () => {
|
||||||
@@ -7,7 +7,7 @@ const loadMenu = async () => {
|
|||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Error('User ID not found');
|
throw new Error('User ID not found');
|
||||||
}
|
}
|
||||||
const response = await axios.get('/api/navigation/' + userId);
|
const response = await apiClient.get('/api/navigation/' + userId);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
73
frontend/src/views/settings/AccountView.vue
Normal file
73
frontend/src/views/settings/AccountView.vue
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>{{ $t("settings.account.title") }}</h2>
|
||||||
|
<div>
|
||||||
|
<label><span>{{ $t("settings.account.username") }} </span><input type="text" v-model="username"
|
||||||
|
:placeholder="$t('settings.account.username')" /></label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label><span>{{ $t("settings.account.email") }} </span><input type="text" v-model="email"
|
||||||
|
:placeholder="$t('settings.account.email')" /></label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label><span>{{ $t("settings.account.newpassword") }} </span><input type="password" v-model="newpassword"
|
||||||
|
:placeholder="$t('settings.account.newpassword')" /></label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label><span>{{ $t("settings.account.newpasswordretype") }} </span><input type="password"
|
||||||
|
v-model="newpasswordretype" :placeholder="$t('settings.account.newpasswordretype')" /></label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label><span>{{ $t("settings.account.oldpassword") }} </span><input type="password"
|
||||||
|
v-model="oldpassword" :placeholder="$t('settings.account.oldpassword')" /></label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button @click="changeAccount">{{ $t("settings.account.changeaction") }}</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label><input type="checkbox" v-model="showInSearch" /> {{ $t("settings.account.showinsearch") }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import apiClient from '@/utils/axios.js';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "AccountSettingsView",
|
||||||
|
components: {},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['user']),
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
username: "",
|
||||||
|
email: "",
|
||||||
|
newpassword: "",
|
||||||
|
newpasswordretype: "",
|
||||||
|
showInSearch: false,
|
||||||
|
oldpassword: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {},
|
||||||
|
async mounted() {
|
||||||
|
const response = await apiClient.post('/api/settings/account', { userId: this.user.id });
|
||||||
|
console.log(response.data);
|
||||||
|
this.username = response.data.username;
|
||||||
|
this.showInSearch = response.data.showinsearch;
|
||||||
|
this.email = response.data.email;
|
||||||
|
console.log(this.showInSearch);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
label {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
label > span {
|
||||||
|
width: 15em;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
17
frontend/src/views/settings/SexualityView.vue
Normal file
17
frontend/src/views/settings/SexualityView.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>{{ $t("settings.sexuality.title") }}</h2>
|
||||||
|
<SettingsWidget :settingsType="'sexuality'" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SettingsWidget from '@/components/SettingsWidget.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SexualitySettingsView',
|
||||||
|
components: {
|
||||||
|
SettingsWidget,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
2840
package-lock.json
generated
Normal file
2840
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
@@ -2,17 +2,20 @@
|
|||||||
"name": "yourpart",
|
"name": "yourpart",
|
||||||
"version": "3.0.0-pre-alpha.0.1",
|
"version": "3.0.0-pre-alpha.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm-run-all --parallel build start:backend",
|
"start": "npm-run-all --parallel build start:backend",
|
||||||
"build": "cd frontend && npm run build",
|
"build": "cd frontend && npm run build",
|
||||||
"start:backend": "cd backend && node server.js",
|
"start:backend": "cd backend && node server.js",
|
||||||
"dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"",
|
"dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"",
|
||||||
"dev:backend": "cd backend && nodemon server.js",
|
"dev:backend": "cd backend && nodemon server.js",
|
||||||
"dev:frontend": "cd frontend && npm run serve"
|
"dev:frontend": "cd frontend && npm run serve"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"concurrently": "^7.0.0",
|
"concurrently": "^7.0.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"nodemon": "^2.0.15",
|
||||||
"nodemon": "^2.0.15"
|
"npm-run-all": "^4.1.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"sequelize-cli": "^6.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user