User rework falta nomes fer prompt de primera contrasenya i generar links

This commit is contained in:
BinarySandia04 2024-09-29 13:49:28 +02:00
parent 8581fb9314
commit b04208106f
16 changed files with 354 additions and 97 deletions

View File

@ -5,7 +5,7 @@ const UserSchema = new Schema({
name: {type: String, required: true},
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
password: { type: String },
date: { type: Date, default: Date.now},
admin: {type: Boolean, default: false},
image: { type: String },

View File

@ -1,13 +1,76 @@
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const rateLimitMiddleware = require("../config/rate-limiter");
const { isAdmin } = require('../config/middleware');
const User = require("../models/User");
router.post('/register', rateLimitMiddleware, (req, res) => {
User.findOne({admin: true}).then((data) => {
if(!data) {
let {
name,
username,
email,
password
} = req.body;
if(!(name && username && email && password)){
res.json({
error: true,
msg: "params"
});
return;
}
User.findOne({username: username}).then((user) => {
if(user){
res.json({
error: true,
msg: "already-exists"
});
} else {
User.findOne({email: email}).then((user) => {
if(user){
res.json({
error: true,
msg: "already-email"
});
} else {
var user = new User({
name: name,
username: username,
password: password,
email: email,
admin: true
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(user.password, salt, (err, hash) => {
if(err) throw err;
user.password = hash;
user.save().then(user => {
res.json({
success: true
});
return;
}).catch((error) => { res.json({ error: true }); return; });
});
})
}
}).catch((error) => { res.json({ error: true, msg: "Hi ha hagut un error intern, prova-ho més tard" }); return; });
}
}).catch((error) => { res.json({ error: true, msg: "Hi ha hagut un error intern, prova-ho més tard" }); return; });
}
});
});
router.get('/users', isAdmin, (req, res) => {
console.log("HJDSHAKJDHKH")
User.find({}).then((users) => {
console.log("HJDSHAKJDHKH2")
res.json({
users
});

View File

@ -8,21 +8,20 @@ const secret = require('../config/keys').secret;
const rateLimitMiddleware = require("../config/rate-limiter");
const { default: jwtDecode } = require('jwt-decode');
const { hasUser } = require('../config/middleware');
const { isAdmin } = require('../config/middleware');
const User = require("../models/User");
const upload = require("../config/storage");
router.post('/register', rateLimitMiddleware, (req, res) => {
router.post('/register', isAdmin, (req, res) => {
let {
name,
username,
email,
password
email
} = req.body;
if(!(name && username && email && password)){
if(!(name && username && email)){
res.json({
error: true,
msg: "params"
@ -47,28 +46,20 @@ router.post('/register', rateLimitMiddleware, (req, res) => {
var user = new User({
name: name,
username: username,
password: password,
email: email,
admin: true
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(user.password, salt, (err, hash) => {
if(err) throw err;
user.password = hash;
user.save().then(user => {
res.json({
success: true
});
return;
}).catch((error) => { res.json({ error: true }); return; });
user.save().then(user => {
res.json({
status: "ok",
user
});
})
}
}).catch((error) => { res.json({ error: true, msg: "Hi ha hagut un error intern, prova-ho més tard" }); return; });
}
}).catch((error) => { res.json({ error: true, msg: "Hi ha hagut un error intern, prova-ho més tard" }); return; });
});
router.post('/login', rateLimitMiddleware, (req, res) => {

View File

@ -4,7 +4,6 @@ import { Disconnect } from './Campaign';
const windows = ref([]);
import LoginWindow from '@/views/windows/LoginWindow.vue'
import RegisterWindow from '@/views/windows/RegisterWindow.vue'
import ExampleWindow from '@/views/windows/ExampleWindow.vue'
import MainMenuWindow from '@/views/windows/MainMenuWindow.vue'
import EditProfileWindow from '@/views/windows/EditProfileWindow.vue'
@ -30,13 +29,16 @@ import DatabaseWindow from '@/views/windows/game/DatabaseWindow.vue'
import AccountManagementWindow from '@/views/windows/settings/AccountManagementWindow.vue'
import PluginManagementWindow from '@/views/windows/settings/PluginManagementWindow.vue'
import PluginWindow from '../views/windows/settings/PluginWindow.vue';
import FirstRegisterWindow from '../views/windows/FirstRegisterWindow.vue';
import RegisterUserWindow from '../views/windows/settings/RegisterUserWindow.vue';
let windowMap = {
test: ExampleWindow,
login: LoginWindow,
main_menu: MainMenuWindow,
welcome: WelcomeWindow,
register: RegisterWindow,
first_register: FirstRegisterWindow,
register_user: RegisterUserWindow,
edit_profile: EditProfileWindow,
settings: SettingsWindow,
campaign_list: CampaignListWindow,
@ -80,8 +82,8 @@ const defValues = {
id: 'login',
title: 'Login',
},
'register': {
id: 'register',
'first_register': {
id: 'first_register',
title: 'register-admin.title',
},
'main_menu': {
@ -101,11 +103,6 @@ const defValues = {
title: "Edit Profile",
close: () => ClearWindow('edit_profile')
},
'settings': {
id: 'settings',
title: 'settings.title',
close: () => ClearWindow('settings')
},
'campaign_list': {
id: 'campaign_list',
title: 'campaigns.title',

View File

@ -1,11 +1,22 @@
<script setup>
import { onMounted, ref, watch } from 'vue';
const props = defineProps(['title', 'img']);
const imgSrc = ref("");
onMounted(() => {
imgSrc.value = props.img;
watch(() => props.img, () => {
imgSrc.value = props.img;
});
})
</script>
<template>
<div class="document centered">
<h1>{{props.title}}</h1>
<img :src="props.img" class="big-icon">
<img :src="imgSrc" class="big-icon">
<br>
<slot></slot>
</div>
@ -16,7 +27,6 @@ const props = defineProps(['title', 'img']);
height: 80px;
width: 80px;
border: 1px solid var(--color-border);
padding: 16px;
margin:auto;
}
</style>

View File

@ -1,15 +1,11 @@
<script setup>
import VersionRender from '@/views/others/VersionRender.vue'
import ErrorMessage from '@/views/others/ErrorMessage.vue'
import { onMounted, ref } from 'vue';
import { GetUser, LogoutUser } from '@/services/User'
import Api from '@/services/Api'
import useEmitter from '@/services/Emitter';
import { ClearWindows, CreateWindow, CreateChildWindow } from '../../services/Windows';
import { ClearWindows, CreateWindow, CreateChildWindow, ClearWindow } from '../../services/Windows';
import { backendUrl } from '../../services/BackendURL';
const emitter = useEmitter();
@ -31,16 +27,24 @@ function LogOut(){
}
function EditProfile(){
CreateChildWindow('main_menu', 'edit_profile');
console.log("User:"); console.log(GetUser());
CreateChildWindow('main_menu', 'edit_profile', {
user: GetUser()
});
}
function EditSettings(){
CreateChildWindow('main_menu', 'settings');
ClearWindow('main_menu');
CreateWindow('settings', {
id: 'settings',
type: 'settings',
title: 'settings.title',
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
});
}
onMounted(() => {
let userAvatarDisplay = document.getElementById("upload-image");
let sendAvatarForm = document.getElementById("send-avatar-form");
let sendAvatarFileUploader = document.getElementById("send-avatar-file-uploader");
sendAvatarFileUploader.addEventListener("change", (event) => {

View File

@ -4,22 +4,32 @@ import WindowHandle from '@/views/partials/WindowHandle.vue';
import { onMounted, onUpdated, ref } from 'vue';
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
import Api from '@/services/Api.js'
import useEmitter from '@/services/Emitter';
const emitter = useEmitter();
import Api from '@/services/Api'
import BigIconTemplate from '../partials/BigIconTemplate.vue';
import { SetMinSize, SetResizable } from '../../services/Windows';
import { backendUrl } from '../../services/BackendURL';
const props = defineProps(['data']);
const data = props.data;
const userIcon = ref("");
const handle = ref(null);
let id = data.id;
console.log(data);
onMounted(() => {
SetupHandle(id, handle);
SetSize(id, {width: 500, height: 380});
SetSize(id, {width: 500, height: 480});
ResetPosition(id, "center");
SetResizable(id, true);
SetMinSize(id, {width: 350, height: 280});
Api().get('/user/retrieve-avatar?username=' + data.user.username).then((response) => {
if(response.data.image) userIcon.value = backendUrl + "public/" + response.data.image;
else userIcon.value = "public/img/def-avatar.jpg";
}).catch((err) => console.log("Internal error"));
});
</script>
@ -27,14 +37,16 @@ onMounted(() => {
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<BigIconTemplate :title="data.user.username" :img="userIcon">
</BigIconTemplate>
</div>
</template>
<style scoped>
.window-wrapper {
min-width: 700px;
min-height: 630px;
display: flex;
align-items: center;

View File

@ -2,17 +2,16 @@
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
import VersionRender from '@/views/others/VersionRender.vue'
import ErrorMessage from '@/views/others/ErrorMessage.vue'
import WindowHandle from '@/views/partials/WindowHandle.vue';
import { onMounted, onUpdated, ref } from 'vue';
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
import { onMounted, ref } from 'vue';
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
import Api from '@/services/Api.js'
import { ClearWindow, ClearWindows, CreateWindow } from '../../services/Windows';
import { DisplayToast } from '../../services/Dragonroll';
import { ClearWindow, CreateWindow } from '@/services/Windows';
import { DisplayToast } from '@/services/Dragonroll';
const email = ref("");
const name = ref("");
@ -28,7 +27,6 @@ const props = defineProps(['data']);
const data = props.data;
let id = data.id;
let title = data.title;
onMounted(() => {
SetupHandle(id, handle);
@ -43,7 +41,7 @@ function register(){
return;
}
Api().post('/user/register',
Api().post('/admin/register',
{
name: name.value,
username: username.value,

View File

@ -1,8 +1,7 @@
<script setup>
import VersionRender from '@/views/others/VersionRender.vue'
import ErrorMessage from '@/views/others/ErrorMessage.vue'
import SuccessMessage from '@/views/others/SuccessMessage.vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
import { onMounted, onUpdated, ref } from 'vue';
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
@ -18,33 +17,27 @@ const handle = ref(null);
const username = ref("");
const password = ref("");
const errorMessage = ref("");
const successMessage = ref("");
const props = defineProps(['data']);
const data = props.data;
let id = data.id;
let title = data.title;
let success = data.msg;
onMounted(() => {
successMessage.value = success;
SetupHandle(id, handle);
SetSize(id, {width: 450, height: 500});
SetSize(id, {width: 450, height: 420});
ResetPosition(id, "center");
});
function login(){
Api().post('/user/login', { username: username.value, password: password.value }).then((response) => {
const data = response.data;
console.log(data);
if(data.error){
errorMessage.value = data.msg;
DisplayToast('red', t(`login.errors.${data.msg}`), 3000)
} else {
errorMessage.value = "";
SetUser(data.token);
ShowMainMenu();
@ -89,8 +82,6 @@ function ShowMainMenu(){
</div>
</form>
<ErrorMessage v-if="errorMessage">{{ errorMessage }}</ErrorMessage>
<SuccessMessage v-if="successMessage">{{ successMessage }}</SuccessMessage>
<!-- <VersionRender></VersionRender> -->
</div>

View File

@ -32,24 +32,6 @@ onMounted(() => {
SetupHandle(id, handle);
SetSize(id, {width: 500, height: 460});
ResetPosition(id, "center", emitter);
/*
AddTooltip(campaignButton.value, "<h2>Hey</h2>Hola test");
AddContextMenu(campaignButton.value, [
{name: "Test", action: () => {alert("Test")}},
{name: "A", action: () => {alert("A")}},
{name: "B", action: () => {alert("B")}},
{name: "Patata", action: () => {alert("Patata")}, context: [
{name: "Test 2dsadsadsdsadasdasdsad", action: () => {alert("Test context")}},
{name: "A 3", action: () => {alert("A context")}, context: [
{name: "Test", action: () => {alert("Test")}},
{name: "A", action: () => {alert("A")}},
{name: "B", action: () => {alert("B")}},
]},
{name: "B 5", action: () => {alert("B context")}},
]},
])
*/
});
function OpenCampaigns(){

View File

@ -7,7 +7,7 @@ import WindowHandle from '@/views/partials/WindowHandle.vue';
import Tabs from '../partials/Tabs.vue';
import Dropdown from '../partials/Dropdown.vue';
import { GetUserSetting, SetUserSetting } from '../../services/User';
import { ClearWindow, CreateChildWindow, SetMaxSize, SetMinSize, SetResizable } from '../../services/Windows';
import { ClearWindow, CreateChildWindow, CreateWindow, SetMaxSize, SetMinSize, SetResizable } from '../../services/Windows';
const handle = ref(null);
@ -48,23 +48,37 @@ onMounted(() => {
});
function OpenManageAccounts(){
CreateChildWindow(id, '', {
ClearWindow('settings');
CreateWindow('account_management', {
type: 'account_management',
title: 'settings.site-administration.manage-accounts.title',
id: 'account-management',
close: () => {
back: () => {
ClearWindow('account-management')
CreateWindow('settings', {
id: 'settings',
type: 'settings',
title: 'settings.title',
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
});
}
})
}
function OpenManagePlugins(){
CreateChildWindow(id, '', {
ClearWindow('settings');
CreateWindow('plugin_management', {
type: 'plugin_management',
title: 'settings.site-administration.manage-plugins.title',
id: 'plugin-management',
close: () => {
back: () => {
ClearWindow('plugin-management')
CreateWindow('settings', {
id: 'settings',
type: 'settings',
title: 'settings.title',
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
});
}
})
}

View File

@ -1,5 +1,5 @@
<script setup>
import { onMounted, ref, shallowRef } from 'vue';
import { onMounted, ref, shallowRef, toRaw } from 'vue';
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
import Api from '@/services/Api'
@ -8,6 +8,7 @@ import IconButton from '@/views/partials/game/IconButton.vue'
import WindowHandle from '@/views/partials/WindowHandle.vue';
import ConceptList from '../../partials/ConceptList.vue';
import { backendUrl } from '../../../services/BackendURL';
import { ClearWindow, CreateChildWindow, CreateWindow } from '../../../services/Windows';
const handle = ref(null);
@ -23,6 +24,10 @@ onMounted(() => {
SetSize(id, {width: 500, height: 380});
ResetPosition(id, "center");
RefreshUsers();
});
function RefreshUsers(){
Api().get('/admin/users').then(response => {
let users = response.data.users;
elements.value = [];
@ -32,15 +37,36 @@ onMounted(() => {
_id: user._id,
info: {
name: user.username
}
},
user
})
});
}).catch((err) => console.log(err));
});
}
async function ElementIcon(element){
let response = await Api().get('/user/retrieve-avatar?username=' + element.name);
if(response.data.image) return backendUrl + "public/" + response.data.image;
return "public/img/def-avatar.jpg";
}
function OpenAccount(data){
CreateChildWindow(id, 'edit_profile', {
user: toRaw(data.user),
close: () => {ClearWindow('edit_profile');}
});
}
function OpenCreateAccount(){
CreateChildWindow(id, 'register_user', {
title: 'register-account.title',
id: 'register_user',
done: () => {
RefreshUsers();
ClearWindow('register_user');
},
close: () => ClearWindow('register_user')
})
}
</script>
@ -53,10 +79,11 @@ async function ElementIcon(element){
<ConceptList
:elements="elements"
:icon="ElementIcon"
:open="OpenAccount"
></ConceptList>
<div class="fixed-bottom-buttons">
<IconButton icon="/icons/iconoir/regular/plus.svg" :action="OpenCreateItemPrompt"></IconButton>
<IconButton icon="/icons/iconoir/regular/plus.svg" :action="OpenCreateAccount"></IconButton>
</div>
</div>
</template>

View File

@ -0,0 +1,135 @@
<script setup>
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
import ErrorMessage from '@/views/others/ErrorMessage.vue'
import WindowHandle from '@/views/partials/WindowHandle.vue';
import { onMounted, ref } from 'vue';
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
import Api from '@/services/Api.js'
import { ClearWindow, CreateWindow } from '@/services/Windows';
import { DisplayToast } from '@/services/Dragonroll';
import { CallWindow } from '../../../services/Windows';
const email = ref("");
const name = ref("");
const username = ref("");
const password = ref("");
const confirmPassword = ref("");
const errorMessage = ref("");
const handle = ref(null);
const props = defineProps(['data']);
const data = props.data;
let id = data.id;
let title = data.title;
onMounted(() => {
SetupHandle(id, handle);
SetSize(id, {width: 500, height: 430});
ResetPosition(id, "center");
});
function register(){
Api().post('/user/register',
{
name: name.value,
username: username.value,
email: email.value,
}).then((response) => {
const data = response.data;
console.log(data);
if(data.error){
console.log(data);
errorMessage.value = data.msg;
} else {
errorMessage.value = "";
console.log("Logged successfully");
DisplayToast('green', 'Account created successfully', 3000);
CallWindow(id, 'done');
}
}).catch((error) => {
console.log(error);
});
}
</script>
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<div class="window-content">
<div class="document" v-html="t('register-account.welcome-message')">
</div>
<form v-on:submit.prevent="register">
<div class="form-field">
<label for="name">{{$t('general.name')}}</label>
<input id="name-field" type="text" :placeholder="t('placeholders.name')" name="name" v-model="name" autocomplete="off" >
</div>
<div class="form-field">
<label for="email">{{$t('general.email')}}</label>
<input id="email-field" type="email" :placeholder="t('placeholders.email')" name="email" v-model="email" autocomplete="off" >
</div>
<div class="form-field">
<label for="username">{{$t('general.username')}}</label>
<input id="username-field" type="text" :placeholder="t('placeholders.username')" name="username" v-model="username" autocomplete="off" >
</div>
<div class="form-field">
<button class="btn-primary sound-click confirm-form-button">{{$t('general.register')}}</button>
</div>
</form>
<ErrorMessage v-if="errorMessage">{{ errorMessage }}</ErrorMessage>
</div>
<!-- <VersionRender></VersionRender> -->
</div>
</template>
<style scoped>
p {
user-select: none;
}
.window-wrapper {
display: flex;
align-items: center;
}
.window-content {
flex-direction: column;
display: flex;
align-items: center;
padding-bottom: 50px;
width: 100%;
padding: 20px;
}
.splash-image {
width: 450px;
user-select: none;
}
form {
width: 100%;
}
label {
text-align: left;
}
</style>

View File

@ -17,6 +17,13 @@
"weight": "Weight",
"price": "Price"
},
"login": {
"errors": {
"params": "You must fill all parameters!",
"wrong": "Wrong user or password",
"internal": "Internal error"
}
},
"placeholders": {
"name": "John Doe",
"email": "john@doe.com",
@ -28,6 +35,10 @@
"title": "Register Admin Account",
"welcome-message": "<h1>Welcome!</h1><b>You have successfull setup Dragonroll!</b><p>Please create the admin account</p><p>Once the admin account has been created, you will be able to create user accounts that will be able to access Dragonroll</p><hr>"
},
"register-account": {
"title": "Create new user account",
"welcome-message": "<h1>Hola</h1>"
},
"main-menu": {
"title": "Dragonroll",
"main-menu": "Main Menu",

View File

@ -17,9 +17,16 @@
"weight": "Weight",
"price": "Price"
},
"login": {
"errors": {
"params": "You must fill all parameters!",
"wrong": "Wrong user or password",
"internal": "Internal error"
}
},
"placeholders": {
"name": "John Doe",
"email": "john@doe.com",
"email": "john{'@'}doe.com",
"username": "Enter your username...",
"password": "Enter your password...",
"password-confirm": "Enter again your password..."
@ -28,6 +35,10 @@
"title": "Register Admin Account",
"welcome-message": "<h1>Welcome!</h1><b>You have successfull setup Dragonroll!</b><p>Please create the admin account</p><p>Once the admin account has been created, you will be able to create user accounts that will be able to access Dragonroll</p><hr>"
},
"register-account": {
"title": "Create new user account",
"welcome-message": "<p>Here you can create a new user account. The first time the user logs in will be prompted to set his password</p><hr>"
},
"main-menu": {
"title": "Dragonroll",
"main-menu": "Main Menu",

View File

@ -17,6 +17,13 @@
"weight": "Weight",
"price": "Price"
},
"login": {
"errors": {
"params": "You must fill all parameters!",
"wrong": "Wrong user or password",
"internal": "Internal error"
}
},
"placeholders": {
"name": "John Doe",
"email": "john@doe.com",
@ -28,6 +35,10 @@
"title": "Registrar cuenta de administrador",
"welcome-message": "<h1>Bienvenido!</h1><b>Has configurado correctamente Dragonroll!</b><p>Porfavor, crea la cuenta de administrador</p><p>Una vez creada, esta cuenta te permitirá configurar el servidor y crear cuentas que puedan acceder a este</p><hr>"
},
"register-account": {
"title": "Create new user account",
"welcome-message": "<h1>Hola</h1>"
},
"main-menu": {
"title": "Dragonroll",
"main-menu": "Menú Principal",