mirror of
https://github.com/janishutz/libreevent.git
synced 2026-03-14 07:00:05 +01:00
Restructuring for new way of installing libreevent
This commit is contained in:
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* libreevent - .eslintrc.js
|
||||
*
|
||||
* Created by Janis Hutz 02/26/2023, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'commonjs': true,
|
||||
'es2021': true,
|
||||
'node': true
|
||||
},
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended'
|
||||
],
|
||||
'overrides': [
|
||||
],
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 'latest'
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
4
|
||||
],
|
||||
'linebreak-style': [
|
||||
'error',
|
||||
'unix'
|
||||
],
|
||||
'quotes': [
|
||||
'error',
|
||||
'single'
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'spaced-comment': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'arrow-spacing': [
|
||||
'error',
|
||||
{ 'before': true, 'after': true }
|
||||
],
|
||||
'func-call-spacing': [
|
||||
'error',
|
||||
'never'
|
||||
],
|
||||
'keyword-spacing': [
|
||||
'error',
|
||||
{ 'before': true, 'after': true }
|
||||
],
|
||||
'key-spacing': [
|
||||
'error',
|
||||
{ 'mode': 'strict' }
|
||||
],
|
||||
'space-before-blocks': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'space-in-parens': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'no-var': 'error'
|
||||
}
|
||||
};
|
||||
28
src/webapp/setup/.gitignore
vendored
28
src/webapp/setup/.gitignore
vendored
@@ -1,28 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
3
src/webapp/setup/.vscode/extensions.json
vendored
3
src/webapp/setup/.vscode/extensions.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
# libreevent-setup
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
@@ -1,15 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>No page title :: libreevent</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200">
|
||||
<script defer src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
2025
src/webapp/setup/package-lock.json
generated
2025
src/webapp/setup/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "libreevent-setup",
|
||||
"version": "1.0.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"pinia": "^2.1.3",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-plugin-vue": "^9.11.0",
|
||||
"vite": "^4.5.3"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,180 +0,0 @@
|
||||
<template>
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition :name="route.meta.transition || 'scale'" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #2c3e50;
|
||||
--accent-background: rgb(30, 30, 82);
|
||||
--secondary-color: white;
|
||||
--background-color: white;
|
||||
--popup-color: rgb(224, 224, 224);
|
||||
--accent-color: #42b983;
|
||||
--hover-color: rgb(165, 165, 165);
|
||||
--accent-background-hover: rgb(124, 140, 236);
|
||||
--overlay-color: rgba(0, 0, 0, 0.7);
|
||||
--inactive-color: rgb(100, 100, 100);
|
||||
--highlight-backdrop: rgb(143, 134, 192);
|
||||
--hint-color: rgb(174, 210, 221);
|
||||
--PI: 3.14159265358979;
|
||||
}
|
||||
|
||||
@media ( prefers-color-scheme: dark ) {
|
||||
:root {
|
||||
--primary-color: white;
|
||||
--accent-background: rgb(56, 56, 112);
|
||||
--secondary-color: white;
|
||||
--background-color: rgb(32, 32, 32);
|
||||
--popup-color: rgb(58, 58, 58);
|
||||
--accent-color: #42b983;
|
||||
--hover-color: rgb(83, 83, 83);
|
||||
--accent-background-hover: #4380a8;
|
||||
--overlay-color: rgba(104, 104, 104, 0.575);
|
||||
--inactive-color: rgb(190, 190, 190);
|
||||
--highlight-backdrop: rgb(85, 63, 207);
|
||||
--hint-color: rgb(88, 91, 110);
|
||||
}
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: var( --highlight-backdrop );
|
||||
color: var( --secondary-color );
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
transition: 0.5s;
|
||||
background-color: var( --background-color );
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: var( --primary-color );
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
font-weight: bold;
|
||||
color: var( --primary-color );
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: #2080ca;
|
||||
}
|
||||
|
||||
.scale-enter-active,
|
||||
.scale-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.scale-enter-from,
|
||||
.scale-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.4s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.material-symbols-outlined {
|
||||
font-variation-settings:
|
||||
'FILL' 0,
|
||||
'wght' 400,
|
||||
'GRAD' 0,
|
||||
'opsz' 48
|
||||
}
|
||||
|
||||
.clr-open {
|
||||
border: black solid 1px !important;
|
||||
}
|
||||
|
||||
.button {
|
||||
margin-top: 2%;
|
||||
background: linear-gradient(90deg, rgb(30, 36, 131), rgb(87, 66, 184), rgb(105, 115, 214), rgb(30, 36, 131), rgb(41, 128, 109), rgb(146, 50, 47));
|
||||
background-size: 300px;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
transition: all 3s;
|
||||
font-size: 75%;
|
||||
color: white;
|
||||
margin-bottom: 5vh;
|
||||
font-size: 125%;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-size: 200%;
|
||||
background-position: -100%;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 50%;
|
||||
padding: 10px;
|
||||
border-radius: 500px;
|
||||
border-style: solid;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
select {
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
border-radius: 500px;
|
||||
border-style: solid;
|
||||
background-color: #b4d9ff;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 60vw;
|
||||
font-size: 110%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
created () {
|
||||
this.theme = localStorage.getItem( 'theme' ) ? localStorage.getItem( 'theme' ) : '';
|
||||
if ( window.matchMedia( '(prefers-color-scheme: dark)' ).matches || this.theme === '☼' ) {
|
||||
document.documentElement.classList.add( 'dark' );
|
||||
this.theme = '☼';
|
||||
} else {
|
||||
document.documentElement.classList.add( 'light' );
|
||||
this.theme = '☽';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 698 KiB |
@@ -1,281 +0,0 @@
|
||||
<!-- eslint-disable no-undef -->
|
||||
<template>
|
||||
<div id="notifications" @click="handleNotifications();">
|
||||
<div class="message-box" :class="[ location, size ]">
|
||||
<div class="message-container" :class="messageType">
|
||||
<span class="material-symbols-outlined types hide" v-if="messageType == 'hide'">question_mark</span>
|
||||
<span class="material-symbols-outlined types" v-else-if="messageType == 'ok'" style="background-color: green;">done</span>
|
||||
<span class="material-symbols-outlined types" v-else-if="messageType == 'error'" style="background-color: red;">close</span>
|
||||
<span class="material-symbols-outlined types progress-spinner" v-else-if="messageType == 'progress'" style="background-color: blue;">progress_activity</span>
|
||||
<span class="material-symbols-outlined types" v-else-if="messageType == 'info'" style="background-color: lightblue;">info</span>
|
||||
<span class="material-symbols-outlined types" v-else-if="messageType == 'warning'" style="background-color: orangered;">warning</span>
|
||||
<p class="message">{{ message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'notifications',
|
||||
props: {
|
||||
location: {
|
||||
type: String,
|
||||
'default': 'topleft',
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
'default': 'default',
|
||||
}
|
||||
// Size options: small, default (default option), big, bigger, huge
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
notifications: {},
|
||||
queue: [],
|
||||
message: '',
|
||||
messageType: 'hide',
|
||||
notificationDisplayTime: 0,
|
||||
notificationPriority: 'normal',
|
||||
currentlyDisplayedNotificationID: 0,
|
||||
currentID: { 'critical': 0, 'medium': 1000, 'low': 100000 },
|
||||
displayTimeCurrentNotification: 0,
|
||||
notificationScheduler: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
createNotification( message, showDuration, messageType, priority ) {
|
||||
/*
|
||||
Takes a notification options array that contains: message, showDuration (in seconds), messageType (ok, error, progress, info) and priority (low, normal, critical).
|
||||
Returns a notification ID which can be used to cancel the notification. The component will throttle notifications and display
|
||||
one at a time and prioritize messages with higher priority. Use vue refs to access these methods.
|
||||
*/
|
||||
let id = 0;
|
||||
|
||||
if ( priority === 'critical' ) {
|
||||
this.currentID[ 'critical' ] += 1;
|
||||
id = this.currentID[ 'critical' ];
|
||||
} else if ( priority === 'normal' ) {
|
||||
this.currentID[ 'medium' ] += 1;
|
||||
id = this.currentID[ 'medium' ];
|
||||
} else if ( priority === 'low' ) {
|
||||
this.currentID[ 'low' ] += 1;
|
||||
id = this.currentID[ 'low' ];
|
||||
}
|
||||
this.notifications[ id ] = { 'message': message, 'showDuration': showDuration, 'messageType': messageType, 'priority': priority, 'id': id };
|
||||
this.queue.push( id );
|
||||
console.log( 'scheduled notification: ' + id + ' (' + message + ')' );
|
||||
if ( this.displayTimeCurrentNotification >= this.notificationDisplayTime ) {
|
||||
this.handleNotifications();
|
||||
}
|
||||
return id;
|
||||
},
|
||||
cancelNotification ( id ) {
|
||||
/*
|
||||
This method deletes a notification and, in case the notification is being displayed, hides it.
|
||||
*/
|
||||
try {
|
||||
delete this.notifications[ id ];
|
||||
} catch ( error ) {
|
||||
console.log( 'notification to be deleted is nonexistent or currently being displayed' );
|
||||
}
|
||||
try {
|
||||
this.queue.splice( this.queue.indexOf( id ), 1 );
|
||||
} catch {
|
||||
console.debug( 'queue empty' );
|
||||
}
|
||||
if ( this.currentlyDisplayedNotificationID == id ) {
|
||||
this.handleNotifications();
|
||||
}
|
||||
},
|
||||
handleNotifications () {
|
||||
/*
|
||||
This methods should NOT be called in any other component than this one!
|
||||
*/
|
||||
this.displayTimeCurrentNotification = 0;
|
||||
this.notificationDisplayTime = 0;
|
||||
this.message = '';
|
||||
this.queue.sort();
|
||||
if ( this.queue.length > 0 ) {
|
||||
this.message = this.notifications[ this.queue[ 0 ] ][ 'message' ];
|
||||
this.messageType = this.notifications[ this.queue[ 0 ] ][ 'messageType' ];
|
||||
this.priority = this.notifications[ this.queue[ 0 ] ][ 'priority' ];
|
||||
this.currentlyDisplayedNotificationID = this.notifications[ this.queue[ 0 ] ][ 'id' ];
|
||||
this.notificationDisplayTime = this.notifications[ this.queue[ 0 ] ][ 'showDuration' ];
|
||||
delete this.notifications[ this.queue[ 0 ] ];
|
||||
this.queue.reverse();
|
||||
this.queue.pop();
|
||||
$( '.message-box' ).css( 'z-index', 20 );
|
||||
} else {
|
||||
this.messageType = 'hide';
|
||||
$( '.message-box' ).css( 'z-index', -1 );
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.notificationScheduler = setInterval( () => {
|
||||
if ( this.displayTimeCurrentNotification >= this.notificationDisplayTime ) {
|
||||
this.handleNotifications();
|
||||
} else {
|
||||
this.displayTimeCurrentNotification += 0.5;
|
||||
}
|
||||
}, 500 );
|
||||
},
|
||||
unmounted ( ) {
|
||||
clearInterval( this.notificationScheduler );
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.message-box {
|
||||
position: fixed;
|
||||
z-index: -1;
|
||||
color: white;
|
||||
transition: all 0.5s;
|
||||
width: 95vw;
|
||||
right: 2.5vw;
|
||||
top: 1vh;
|
||||
height: 10vh;
|
||||
}
|
||||
|
||||
|
||||
.message-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
transition: all 0.5s;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.types {
|
||||
color: white;
|
||||
border-radius: 100%;
|
||||
margin-right: auto;
|
||||
margin-left: 5%;
|
||||
padding: 1.5%;
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-right: 5%;
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.ok {
|
||||
background-color: rgb(1, 71, 1);
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: rgb(114, 1, 1);
|
||||
}
|
||||
|
||||
.info {
|
||||
background-color: rgb(44, 112, 151);
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.hide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.progress {
|
||||
z-index: 20;
|
||||
background-color: rgb(0, 0, 99);
|
||||
}
|
||||
|
||||
.progress-spinner {
|
||||
animation: spin 2s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate( 0deg );
|
||||
}
|
||||
to {
|
||||
transform: rotate( 720deg );
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 750px) {
|
||||
|
||||
.default {
|
||||
height: 10vh;
|
||||
width: 32vw;
|
||||
}
|
||||
|
||||
.small {
|
||||
height: 7vh;
|
||||
width: 27vw;
|
||||
}
|
||||
|
||||
.big {
|
||||
height: 12vh;
|
||||
width: 38vw;
|
||||
}
|
||||
|
||||
.bigger {
|
||||
height: 15vh;
|
||||
width: 43vw;
|
||||
}
|
||||
|
||||
.huge {
|
||||
height: 20vh;
|
||||
width: 50vw;
|
||||
}
|
||||
|
||||
.topleft {
|
||||
top: 3vh;
|
||||
left: 0.5vw;
|
||||
}
|
||||
|
||||
.topright {
|
||||
top: 3vh;
|
||||
right: 0.5vw;
|
||||
}
|
||||
|
||||
.bottomright {
|
||||
bottom: 3vh;
|
||||
right: 0.5vw;
|
||||
}
|
||||
|
||||
.bottomleft {
|
||||
top: 3vh;
|
||||
right: 0.5vw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (min-width: 1500px) {
|
||||
.default {
|
||||
height: 10vh;
|
||||
width: 15vw;
|
||||
}
|
||||
|
||||
.small {
|
||||
height: 7vh;
|
||||
width: 11vw;
|
||||
}
|
||||
|
||||
.big {
|
||||
height: 12vh;
|
||||
width: 17vw;
|
||||
}
|
||||
|
||||
.bigger {
|
||||
height: 15vh;
|
||||
width: 20vw;
|
||||
}
|
||||
|
||||
.huge {
|
||||
height: 20vh;
|
||||
width: 25vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +0,0 @@
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
|
||||
const app = createApp( App );
|
||||
|
||||
app.use( createPinia() );
|
||||
app.use( router );
|
||||
|
||||
app.mount( '#app' );
|
||||
@@ -1,44 +0,0 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import { useBackendStore } from '@/stores/backendStore';
|
||||
import HomeView from '../views/HomeView.vue';
|
||||
import setupRoutes from './setupRoutes';
|
||||
|
||||
let routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView,
|
||||
meta: {
|
||||
title: 'Welcome to libreevent!'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/admin/login',
|
||||
name: 'adminLogin',
|
||||
component: () => import( '../views/AdminLoginView.vue' ),
|
||||
meta: {
|
||||
title: 'Admin login - Restart required! :: libreevent'
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
routes.push( setupRoutes );
|
||||
|
||||
const router = createRouter( {
|
||||
history: createWebHistory( import.meta.env.BASE_URL ),
|
||||
routes: routes
|
||||
} );
|
||||
|
||||
router.beforeEach( ( to ) => {
|
||||
let backendStore = useBackendStore();
|
||||
backendStore.loadVisitedSetupPages();
|
||||
if ( to.name.substring( 0, 5 ) === 'setup' && !backendStore.getVisitedSetupPages[ to.name.substring( 5 ).toLowerCase() ] && to.name.substring( 5 ).toLowerCase() !== 'start' && to.name.substring( 5 ).toLowerCase() !== 'root' ) {
|
||||
return { name: 'setupStart' };
|
||||
}
|
||||
} );
|
||||
|
||||
router.afterEach( ( to ) => {
|
||||
document.title = to.meta.title ? to.meta.title : 'libreevent';
|
||||
} );
|
||||
|
||||
export default router;
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* libreevent - setupRoutes.js
|
||||
*
|
||||
* Created by Janis Hutz 05/14/2023, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
export default {
|
||||
path: '/setup',
|
||||
name: 'setup',
|
||||
component: () => import( '../views/SetupView.vue' ),
|
||||
meta: {
|
||||
title: 'Setup :: Admin - libreevent',
|
||||
setupAuthRequired: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'setupStart',
|
||||
component: () => import( '../views/SetupStartView.vue' ),
|
||||
meta: {
|
||||
title: 'Start :: Setup - libreevent',
|
||||
setupAuthRequired: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'basics',
|
||||
name: 'setupBasics',
|
||||
component: () => import( '../views/BasicSetupView.vue' ),
|
||||
meta: {
|
||||
title: 'Basic setup :: Setup - libreevent',
|
||||
setupAuthRequired: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'root',
|
||||
name: 'setupRoot',
|
||||
component: () => import( '../views/SetupRootView.vue' ),
|
||||
meta: {
|
||||
title: 'Root account :: Setup - libreevent',
|
||||
setupAuthRequired: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'complete',
|
||||
name: 'setupComplete',
|
||||
component: () => import( '../views/SetupCompleteView.vue' ),
|
||||
meta: {
|
||||
title: 'Setup complete :: Setup - libreevent',
|
||||
setupAuthRequired: true,
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* libreevent - backendStore.js
|
||||
*
|
||||
* Created by Janis Hutz 05/14/2023, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useBackendStore = defineStore( 'backend', {
|
||||
state: () => ( { 'visitedSetupPages': {} } ),
|
||||
getters: {
|
||||
getVisitedSetupPages: ( state ) => state.visitedSetupPages,
|
||||
},
|
||||
actions: {
|
||||
addVisitedSetupPages ( page, data ) {
|
||||
this.visitedSetupPages[ page ] = data;
|
||||
sessionStorage.setItem( 'visitedSetupPages', JSON.stringify( this.visitedSetupPages ) );
|
||||
},
|
||||
loadVisitedSetupPages () {
|
||||
this.visitedSetupPages = sessionStorage.getItem( 'visitedSetupPages' ) ? JSON.parse( sessionStorage.getItem( 'visitedSetupPages' ) ) : {};
|
||||
}
|
||||
}
|
||||
} );
|
||||
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<h1>Please restart libreevent!</h1>
|
||||
<p>Restart the node.js app that libreevent runs in to access the login page. Not sure how to do this? Click <a href="https://libreevent.janishutz.com/docs/setup/setup#complete" target="_blank">here</a></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 400%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
created() {
|
||||
fetch( '/getSetupStatus' ).then( res => {
|
||||
if ( res.status === 200 ) {
|
||||
res.text().then( text => {
|
||||
if ( text !== 'true' ) {
|
||||
this.$router.push( '/' );
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,164 +0,0 @@
|
||||
<!--
|
||||
* libreevent - BasicSetupView.vue
|
||||
*
|
||||
* Created by Janis Hutz 08/23/2023, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="content">
|
||||
<h1>Basic Setup</h1>
|
||||
<p>To make setting up the database and email accounts easier, you can enter the required values below.</p>
|
||||
<p>You may find more infos about this part <a href="https://libreevent.janishutz.com/docs/setup/setup#basic-setup" target="_blank">here</a></p>
|
||||
|
||||
<h2>General information</h2>
|
||||
<label for="name">Name of the website</label><br>
|
||||
<input type="text" name="name" id="name" v-model="formData.websiteName"><br>
|
||||
<label for="name">URL of the website</label><br>
|
||||
<input type="text" name="domain" id="domain" v-model="formData.yourDomain"><br>
|
||||
|
||||
|
||||
<h2>Database</h2>
|
||||
<p>A database is a piece of software that specializes in storing data. libreevent can use most SQL based databases as well as a custom JSON-based database. You are strongly encouraged to use a SQL based database, as they perform significantly better. Read more
|
||||
<a href="https://libreevent.janishutz.com/docs/setup/installation#database" target="_blank">here</a>
|
||||
</p>
|
||||
<label for="dbType">Database type</label><br>
|
||||
<select name="dbType" id="dbType" v-model="formData.dbType">
|
||||
<option value="mysql">SQL-Database</option>
|
||||
<option value="json">JSON-Database</option>
|
||||
</select>
|
||||
<form v-if="formData.dbType === 'mysql'">
|
||||
<label for="host">Database host name (usually domain name or IP of webserver)</label><br>
|
||||
<input type="url" name="host" id="host" v-model="formData.db.host"><br>
|
||||
<label for="database">Database name</label><br>
|
||||
<input type="text" name="database" id="database" v-model="formData.db.database"><br>
|
||||
<label for="user">Database user</label><br>
|
||||
<input type="text" name="user" id="user" v-model="formData.db.user"><br>
|
||||
<label for="password">Password</label><br>
|
||||
<input type="password" name="password" id="password" v-model="formData.db.password"><br>
|
||||
<label for="port">Database port (default usually fine)</label><br>
|
||||
<input type="number" name="port" id="port" min="1" max="65535" v-model="formData.db.port"><br>
|
||||
</form>
|
||||
<h2>Email</h2>
|
||||
<p>An email address is required for libreevent to send out mails to users automatically, including their ticket and, in case Two-Factor-Authentication is enabled,
|
||||
a Two-Factor-Authentication email.</p>
|
||||
|
||||
<h3>Account</h3>
|
||||
<p>Here you have to enter the connection details for an email account. Most webhosting plans come with email addresses, so you might as well create a new one.
|
||||
Note that you can customize how the sender of the mail appears down below in the display section.</p>
|
||||
<form>
|
||||
<label for="host">SMTP Server</label><br>
|
||||
<input type="url" name="host" id="host" v-model="formData.email.host"><br>
|
||||
<label for="port">SMTP Port (default usually fine)</label><br>
|
||||
<input type="number" name="port" id="port" min="1" max="65535" v-model="formData.email.port"><br>
|
||||
<label for="user">Email account name</label><br>
|
||||
<input type="email" name="user" id="user" v-model="formData.email.user"><br>
|
||||
<label for="pass">Password</label><br>
|
||||
<input type="password" name="pass" id="pass" v-model="formData.email.pass"><br>
|
||||
</form>
|
||||
<h3>Display</h3>
|
||||
<p>Here you can adjust how the email sender appears to the customer. This also means, that the email address shown below might receive a response if
|
||||
a customer does not possess the ability to read, which might happen from time to time. All mails contain the information that one should not respond
|
||||
to them.
|
||||
</p>
|
||||
<form>
|
||||
<label for="display">Display name (what is shown to user in from field)</label><br>
|
||||
<input type="url" name="display" id="display" v-model="formData.display"><br>
|
||||
<label for="dpEmail">Email address to show</label><br>
|
||||
<input type="text" name="dpEmail" id="dpEmail" v-model="formData.dpEmail"><br>
|
||||
</form>
|
||||
<button @click="submit()" class="button">Continue</button>
|
||||
</div>
|
||||
<notifications ref="notification" location="topright" size="bigger"></notifications>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#dbType {
|
||||
margin-bottom: 5%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { useBackendStore } from '@/stores/backendStore.js';
|
||||
import { mapStores } from 'pinia';
|
||||
import notifications from '../components/notifications.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
notifications,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
formData: {
|
||||
'dbType': 'mysql',
|
||||
'db': {
|
||||
'port': 3306,
|
||||
},
|
||||
'email': {
|
||||
'port': 587
|
||||
},
|
||||
'websiteName': 'libreevent',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores( useBackendStore )
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
if ( this.formData.dbType === 'mysql' ) {
|
||||
if ( !this.formData.db.port || !this.formData.db.host || !this.formData.db.database || !this.formData.db.user || !this.formData.db.password ) {
|
||||
this.$refs.notification.createNotification( 'Database settings are not complete!', 5, 'error', 'normal' );
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( this.formData.email.port && this.formData.email.host && this.formData.email.user && this.formData.email.pass
|
||||
&& this.formData.dpEmail && this.formData.display && this.formData.websiteName ) {
|
||||
this.formData.mailDisplay = this.formData.display + ' <' + this.formData.dpEmail + '>';
|
||||
let progressNot = this.$refs.notification.createNotification( 'Setting up...', 20, 'progress', 'normal' );
|
||||
const options = {
|
||||
method: 'post',
|
||||
body: JSON.stringify( this.formData ),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'charset': 'utf-8'
|
||||
}
|
||||
};
|
||||
fetch( '/setup/saveBasicSettings', options ).then( res => {
|
||||
if ( res.status === 200 ) {
|
||||
this.$refs.notification.cancelNotification( progressNot );
|
||||
this.$refs.notification.createNotification( 'Saved!', 5, 'ok', 'normal' );
|
||||
setTimeout( () => {
|
||||
this.continue();
|
||||
}, 2000 );
|
||||
} else {
|
||||
this.$refs.notification.createNotification( 'Setup key incorrect!', 5, 'error', 'normal' );
|
||||
}
|
||||
} );
|
||||
} else {
|
||||
this.$refs.notification.createNotification( 'Missing entries', 5, 'error', 'normal' );
|
||||
return;
|
||||
}
|
||||
},
|
||||
continue () {
|
||||
sessionStorage.setItem( 'basics', JSON.stringify( this.formData ) );
|
||||
this.backendStore.addVisitedSetupPages( 'root', true );
|
||||
this.$router.push( '/setup/root' );
|
||||
},
|
||||
collectUrl() {
|
||||
this.formData.yourDomain = location.protocol + '//' + location.host + ( location.port ? ':' + location.port : '' );
|
||||
this.formData.db.host = location.hostname;
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if ( sessionStorage.getItem( 'basics' ) ) {
|
||||
this.formData = JSON.parse( sessionStorage.getItem( 'basics' ) );
|
||||
}
|
||||
this.collectUrl();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,86 +0,0 @@
|
||||
<!--
|
||||
* libreevent - HomeView.vue
|
||||
*
|
||||
* Created by Janis Hutz 07/17/2023, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="home">
|
||||
<img alt="Libreevent logo" src="../assets/logo.png">
|
||||
<h1>Welcome to libreǝvent!</h1>
|
||||
<p>Let's start the setup by entering the setup key below! You may define a setup key in the <i>setupkey.txt</i> file of libreevent. See <a href="https://libreevent.janishutz.com/docs/setup/installation" target="_blank">here</a> for more instructions</p>
|
||||
<form>
|
||||
<label for="key">Your setup key</label><br>
|
||||
<input type="text" v-model="formData[ 'token' ]" required name="key" id="key">
|
||||
</form>
|
||||
<button @click="setup();" class="button">Start setup</button>
|
||||
<notifications ref="notification" location="topright" size="bigger"></notifications>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import notifications from '../components/notifications.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
formData: {},
|
||||
};
|
||||
},
|
||||
components: {
|
||||
notifications,
|
||||
},
|
||||
methods: {
|
||||
setup () {
|
||||
const options = {
|
||||
method: 'post',
|
||||
body: JSON.stringify( this.formData ),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'charset': 'utf-8'
|
||||
}
|
||||
};
|
||||
fetch( '/setup/start', options ).then( res => {
|
||||
if ( res.status === 200 ) {
|
||||
this.$router.push( '/setup' );
|
||||
} else {
|
||||
this.$refs.notification.createNotification( 'Setup key incorrect!', 5, 'error', 'normal' );
|
||||
}
|
||||
} );
|
||||
}
|
||||
},
|
||||
created() {
|
||||
fetch( '/setup/getKeyStatus' ).then( res => {
|
||||
if ( res.status === 200 ) {
|
||||
res.text().then( text => {
|
||||
if ( text === 'ok' ) {
|
||||
this.$router.push( '/setup' );
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
img {
|
||||
height: 40vh;
|
||||
}
|
||||
|
||||
.home {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 50%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,60 +0,0 @@
|
||||
<!--
|
||||
* libreevent - SetupCompleteView.vue
|
||||
*
|
||||
* Created by Janis Hutz 05/14/2023, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="content">
|
||||
<img src="@/assets/logo.png" alt="libreevent logo" style="height: 30vh;">
|
||||
<h1>Setup complete!</h1>
|
||||
<p>Congratulations on finishing the setup of libreǝvent!</p>
|
||||
<p>Please restart the node.js application to have it load the actual user interface for libreevent and finish setup.
|
||||
You may then log in at <a :href="windowURL" target="_blank">{{ windowURL }}</a>.
|
||||
You may open that link now and then come back to that page once libreevent is restarted.</p>
|
||||
<p>In the admin panel, there are a few things you still need to change. You may find a list of all things
|
||||
<a href="https://libreevent.janishutz.com/docs/setup/afterSetup" target="_blank">here</a></p>
|
||||
<div class="list-wrapper">
|
||||
<ul>
|
||||
<li>Customize the start page</li>
|
||||
<li>Choose a payment gateway and set it up</li>
|
||||
<li>Create an event location and an event</li>
|
||||
<li>Create other admin accounts with less privileges</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button class="button" @click="cleanup()">Done</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
ul {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.list-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
windowURL: location.protocol + '//' + location.host +'/admin/login',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
cleanup() {
|
||||
sessionStorage.removeItem( 'basics' );
|
||||
sessionStorage.removeItem( 'root' );
|
||||
this.$router.push( '/admin/login' );
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,273 +0,0 @@
|
||||
<!--
|
||||
* libreevent - SetupRootView.vue
|
||||
*
|
||||
* Created by Janis Hutz 05/14/2023, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="content">
|
||||
<h1>Root account</h1>
|
||||
<p>The root account is the most powerful account. Therefore, it should only be used if really necessary and should have a strong password.
|
||||
It also always requires Two Factor Authentication for added security.
|
||||
You may log into the root account by typing 'root' into the Email-Address field on the admin login screen.
|
||||
Therefore, the email used for the root account may also be used for an additional admin account.</p>
|
||||
<p>You may find more infos about this part <a href="https://libreevent.janishutz.com/docs/setup/setup#root-account" target="_blank">here</a></p>
|
||||
<p>By default (when the toggle "Enforce password requirements" below is enabled), libreevent forces you to follow the password requirements listed below.
|
||||
You may turn off those password requirements and use any password, but we strongly advice against this.</p>
|
||||
<ul style="list-style: none;">
|
||||
<li>At least 15 characters long</li>
|
||||
<li>At least 2 special characters</li>
|
||||
<li>At least 2 numbers</li>
|
||||
<li>At least 2 lower and 2 upper case letters</li>
|
||||
</ul>
|
||||
<form>
|
||||
<label for="mail">Email address for 2FA</label><br>
|
||||
<input type="email" name="mail" id="mail" v-model="formData.mail" @keyup="emailLiveChecker()"><br>
|
||||
<p v-if="emailStatus" class="email-status">{{ emailStatus }}</p>
|
||||
<label for="password">Password</label><br>
|
||||
<input type="password" name="password" id="password" v-model="formData.password"><br>
|
||||
<label for="password2">Confirm password</label><br>
|
||||
<input type="password" name="password2" id="password2" v-model="formData.password2"><br>
|
||||
<label for="pwCheck">Enforce password requirements (leaving this turned on is strongly recommended)</label><br>
|
||||
<label class="switch">
|
||||
<input type="checkbox" v-model="passwordCheck">
|
||||
<span class="slider round"></span>
|
||||
</label><br>
|
||||
</form>
|
||||
<button @click="submit()" class="button">Continue</button>
|
||||
</div>
|
||||
<notifications ref="notification" location="topright" size="bigger"></notifications>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useBackendStore } from '@/stores/backendStore.js';
|
||||
import { mapStores } from 'pinia';
|
||||
import notifications from '../components/notifications.vue';
|
||||
|
||||
const lookup = [ '@', '!', '.', ',', '?', '%', '&', '-', '_', ':', ';', '*', '§', '<', '>', '{', '}', '[', ']', '(', ')', '/', '#' ];
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
formData: {},
|
||||
passwordCheck: true,
|
||||
emailStatus: '',
|
||||
};
|
||||
},
|
||||
components: {
|
||||
notifications,
|
||||
},
|
||||
computed: {
|
||||
...mapStores( useBackendStore )
|
||||
},
|
||||
methods: {
|
||||
emailLiveChecker () {
|
||||
setTimeout( () => {
|
||||
if ( this.checkEmail() ) {
|
||||
this.emailStatus = '';
|
||||
} else {
|
||||
this.emailStatus = 'Invalid email address';
|
||||
}
|
||||
}, 100 );
|
||||
},
|
||||
checkEmail () {
|
||||
const mail = this.formData.mail ?? '';
|
||||
let stat = { 'atPos': 0, 'topLevelPos': 0 };
|
||||
for ( let l in mail ) {
|
||||
if ( stat[ 'atPos' ] > 0 ) {
|
||||
if ( mail[ l ] === '@' ) {
|
||||
return false;
|
||||
} else if ( mail[ l ] === '.' ) {
|
||||
if ( stat[ 'topLevelPos' ] > 0 ) {
|
||||
if ( l > stat[ 'topLevelPos' ] + 2 ) {
|
||||
stat[ 'topLevelPos' ] = parseInt( l );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ( l > stat[ 'atPos' ] + 2 ) {
|
||||
stat[ 'topLevelPos' ] = parseInt( l );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '-' || mail[ l ] === '_' ) ) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ( mail[ l ] === '@' ) {
|
||||
if ( l > 2 ) {
|
||||
stat[ 'atPos' ] = parseInt( l );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '.' || mail[ l ] === '-' || mail[ l ] == '_' ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( mail.length > stat[ 'topLevelPos' ] + 2 && stat[ 'topLevelPos' ] > 0 && stat[ 'atPos' ] > 0 ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
submit () {
|
||||
if ( this.formData.mail && this.formData.password && this.formData.password2 ) {
|
||||
if ( this.checkEmail() ) {
|
||||
if ( this.formData.password == this.formData.password2 ) {
|
||||
if ( this.passwordCheck ) {
|
||||
let requirementsCount = { 'special': 0, 'numbers': 0, 'lower': 0, 'upper': 0, 'incorrect': '' };
|
||||
const pw = this.formData.password;
|
||||
for ( let l in pw ) {
|
||||
if ( /[a-z]/.test( pw[ l ] ) ) {
|
||||
requirementsCount[ 'lower' ] += 1;
|
||||
} else if ( /[A-Z]/.test( pw[ l ] ) ) {
|
||||
requirementsCount[ 'upper' ] += 1;
|
||||
} else if ( lookup.includes( pw[ l ] ) ) {
|
||||
requirementsCount[ 'special' ] += 1;
|
||||
} else if ( !isNaN( pw[ l ] ) ) {
|
||||
requirementsCount[ 'numbers' ] += 1;
|
||||
} else {
|
||||
console.log( 'incorrect letter' );
|
||||
requirementsCount[ 'incorrect' ] = pw[ l ];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( requirementsCount[ 'incorrect' ] ) {
|
||||
this.$refs.notification.createNotification( `Character "${ requirementsCount[ 'incorrect' ] }" cannot be used for passwords`, 5, 'error', 'normal' );
|
||||
} else {
|
||||
if ( pw.length > 14 ) {
|
||||
if ( requirementsCount[ 'lower' ] > 1 && requirementsCount[ 'upper' ] > 1 && requirementsCount[ 'special' ] > 1 && requirementsCount[ 'numbers' ] > 1 ) {
|
||||
this.proceed();
|
||||
} else {
|
||||
this.$refs.notification.createNotification( 'Your password does not fulfill the requirements', 5, 'error', 'normal' );
|
||||
}
|
||||
} else {
|
||||
this.$refs.notification.createNotification( 'Your password is not long enough', 5, 'error', 'normal' );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( confirm( 'Do you really want to proceed without having your password checked? This is strongly discouraged as it essentially removes the first factor (the password) of the authentication making it much less secure.' ) ) {
|
||||
if ( confirm( 'Are you really sure?' ) ) {
|
||||
this.proceed();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.$refs.notification.createNotification( 'Passwords do not match', 10, 'error', 'normal' );
|
||||
}
|
||||
} else {
|
||||
this.$refs.notification.createNotification( 'The email address you entered is not an email address', 10, 'error', 'normal' );
|
||||
}
|
||||
} else {
|
||||
this.$refs.notification.createNotification( 'One or more fields missing!', 10, 'error', 'normal' );
|
||||
}
|
||||
},
|
||||
proceed () {
|
||||
let progressNot = this.$refs.notification.createNotification( 'Saving...', 20, 'progress', 'normal' );
|
||||
const options = {
|
||||
method: 'post',
|
||||
body: JSON.stringify( this.formData ),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'charset': 'utf-8'
|
||||
}
|
||||
};
|
||||
fetch( '/setup/saveRootAccount', options ).then( res => {
|
||||
if ( res.status === 200 ) {
|
||||
this.$refs.notification.cancelNotification( progressNot );
|
||||
this.$refs.notification.createNotification( 'Saved!', 5, 'ok', 'normal' );
|
||||
setTimeout( () => {
|
||||
sessionStorage.setItem( 'basics', JSON.stringify( this.formData ) );
|
||||
this.backendStore.addVisitedSetupPages( 'complete', true );
|
||||
this.$router.push( 'complete' );
|
||||
}, 2000 );
|
||||
} else {
|
||||
this.$refs.notification.createNotification( 'Setup key incorrect!', 5, 'error', 'normal' );
|
||||
}
|
||||
} );
|
||||
},
|
||||
},
|
||||
created () {
|
||||
if ( sessionStorage.getItem( 'root' ) ) {
|
||||
this.formData = JSON.parse( sessionStorage.getItem( 'root' ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.email-status {
|
||||
margin-top: -10px;
|
||||
color: red;
|
||||
font-style: italic;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_switch */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px #2196F3;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,43 +0,0 @@
|
||||
<!--
|
||||
* libreevent - SetupStartView.vue
|
||||
*
|
||||
* Created by Janis Hutz 05/14/2023, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="content">
|
||||
<img src="@/assets/logo.png" alt="libreevent logo" style="height: 40vh;">
|
||||
<h1 class="title">Welcome to libreǝvent!</h1>
|
||||
<i style="font-size: small;">All links during setup open in separate tabs</i>
|
||||
<p>Thank you for downloading libreǝvent, the free & open source event management solution. libreǝvent aims to help you save both time and money when hosting events, so you can focus on what really matters.</p>
|
||||
<p>Let's start by setting it up. We strongly encourage you to do the setup (and all administrative tasks for that matter) on a PC and to also have a look at the extensive documentation of the setup process <a href="https://libreevent.janishutz.com/docs/setup/setup" target="_blank">here</a></p>
|
||||
<button @click="setup();" class="button">Start setup</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useBackendStore } from '@/stores/backendStore.js';
|
||||
import { mapStores } from 'pinia';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
formData: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores( useBackendStore )
|
||||
},
|
||||
methods: {
|
||||
setup () {
|
||||
this.backendStore.addVisitedSetupPages( 'basics', true );
|
||||
this.$router.push( '/setup/basics' );
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,77 +0,0 @@
|
||||
<!--
|
||||
* libreevent - SetupView.vue
|
||||
*
|
||||
* Created by Janis Hutz 05/14/2023, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<nav class="setup-nav">
|
||||
<router-link to="/setup">Start</router-link> |
|
||||
<router-link to="/setup/basics" v-if="backendStore.getVisitedSetupPages[ 'basics' ]">Basic setup</router-link>
|
||||
<a v-else class="inactive">Basic Setup</a> |
|
||||
<router-link to="/setup/root" v-if="backendStore.getVisitedSetupPages[ 'root' ]">Root account</router-link>
|
||||
<a v-else class="inactive">Root account</a> |
|
||||
<router-link to="/setup/complete" v-if="backendStore.getVisitedSetupPages[ 'complete' ]">Complete</router-link>
|
||||
<a v-else class="inactive">Complete</a>
|
||||
</nav>
|
||||
<div class="main-view">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition :name="route.meta.transition || 'scale'" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useBackendStore } from '@/stores/backendStore.js';
|
||||
import { mapStores } from 'pinia';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
formData: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores( useBackendStore )
|
||||
},
|
||||
created () {
|
||||
this.backendStore.loadVisitedSetupPages();
|
||||
fetch( '/setup/getKeyStatus' ).then( res => {
|
||||
if ( res.status === 200 ) {
|
||||
res.text().then( text => {
|
||||
if ( text != 'ok' ) {
|
||||
this.$router.push( '/' );
|
||||
}
|
||||
} );
|
||||
} else {
|
||||
this.$router.push( '/' );
|
||||
}
|
||||
} );
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.inactive {
|
||||
color: var( --inactive-color );
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.setup-nav {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,19 +0,0 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig( {
|
||||
plugins: [
|
||||
vue(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath( new URL( './src', import.meta.url ) )
|
||||
},
|
||||
},
|
||||
server: {
|
||||
'port': 8081
|
||||
}
|
||||
} );
|
||||
Reference in New Issue
Block a user