added authentication

This commit is contained in:
WeeXnes 2025-06-07 21:13:57 +02:00
parent 0504c7f8c5
commit 30c00b46df
10 changed files with 180 additions and 7 deletions

View file

@ -0,0 +1,42 @@
const bcrypt = require('bcryptjs');
const fs = require('fs');
const path = require('path');
const password = process.argv[2];
if (!password) {
console.log('Please provide a password as a command-line argument.');
process.exit(1);
}
const saltRounds = 10;
const configFilePath = path.join(__dirname, '../panel.config.ts');
bcrypt.hash(password, saltRounds)
.then(hash => {
console.log('Generated bcrypt hash:', hash);
fs.readFile(configFilePath, 'utf8', (err, data) => {
if (err) {
console.error('Error reading the config file:', err);
process.exit(1);
}
const passwordHashRegex = /password_hash:\s*"[^"]*"/;
const updatedData = data.replace(passwordHashRegex, `password_hash: "${hash}"`) || data.replace(/(password_hash:\s*".*")/, `password_hash: "${hash}"`);
fs.writeFile(configFilePath, updatedData, 'utf8', (err) => {
if (err) {
console.error('Error writing the config file:', err);
process.exit(1);
}
console.log('Updated the password hash in panel.config.ts');
});
});
})
.catch(err => {
console.error('Error generating hash:', err);
});

View file

@ -0,0 +1,11 @@
{
"name": "password_hash_gen",
"version": "1.0.0",
"main": "generator.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": ""
}

View file

@ -1,20 +1,20 @@
import type {MinecraftServer} from "~/types/MinecraftServer";
import type {SettingsJsonFile} from "~/types/SettingsJsonFile";
import * as os from 'os';
import { reactive } from "vue";
import {saveJsonFile} from "~/util/jsonLoader";
export namespace environment{
export const jwt_globals = reactive({
secret: "tH5kunVlY4rDIv10Y6Hy0NoLKpH1uYtW",
});
export namespace paths{
export let data: string = `${os.homedir()}/.mcservermanager`;
export let servers: string = `${os.homedir()}/.mcservermanager/servers`;
}
export namespace files{
export let settings: string = paths.data + "/settings.json";
}
export let settings: SettingsJsonFile;
}

9
middleware/auth.ts Normal file
View file

@ -0,0 +1,9 @@
import axios from "axios";
export default defineNuxtRouteMiddleware(async (to, from) => {
const user = useCookie('token')
if (!user.value) {
return navigateTo('/login')
}
})

View file

@ -7,20 +7,24 @@
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
"postinstall": "nuxt prepare",
"password_gen": "node .password_hash_gen/generator.js"
},
"dependencies": {
"@nuxt/icon": "^1.10.3",
"@nuxtjs/tailwindcss": "^6.13.1",
"axios": "^1.7.9",
"bcryptjs": "^3.0.2",
"daisyui": "^4.12.23",
"execa": "^9.5.2",
"ini": "^5.0.0",
"jsonwebtoken": "^9.0.2",
"nuxt": "^3.15.4",
"vue": "latest",
"vue-router": "latest"
},
"devDependencies": {
"@types/ini": "^4.1.1"
"@types/ini": "^4.1.1",
"@types/jsonwebtoken": "^9.0.9"
}
}

View file

@ -462,6 +462,11 @@ onBeforeUnmount(()=>{
})
definePageMeta({
middleware: 'auth'
})
</script>

65
pages/login.vue Normal file
View file

@ -0,0 +1,65 @@
<template>
<div class="flex items-center justify-center py-16">
<div class="card w-96 bg-base-100 shadow-2xl p-6">
<h2 class="text-2xl font-bold text-center">Login</h2>
<form @submit.prevent="handleLogin" class="mt-4">
<div class="form-control mt-2">
<label class="label">
<span class="label-text">Password</span>
</label>
<input
v-model="password"
type="password"
placeholder="Enter your password"
class="input input-bordered"
required
/>
</div>
<div class="form-control mt-4">
<button type="submit" class="btn btn-primary w-full">Login</button>
</div>
</form>
</div>
</div>
</template>
<script lang="ts">
import { ref } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
export default {
data() {
return {
email: '',
password: ''
};
},
methods: {
async handleLogin() {
try {
const response = await axios.post('/api/login', {
password: this.password,
});
console.log(response.data);
if (response?.data?.message == 'Login successful!') {
const token = response.data.token;
const cookie = useCookie('token');
cookie.value = token;
this.$router.push('/');
} else {
console.error('Login failed');
}
} catch (error) {
console.error('Login error:', error);
}
}
}
};
</script>

5
panel.config.ts Normal file
View file

@ -0,0 +1,5 @@
import { reactive } from "vue";
export const settings = reactive({
password_hash: ""
});

32
server/api/login.ts Normal file
View file

@ -0,0 +1,32 @@
import bcrypt from 'bcryptjs';
import { sendError, createError } from 'h3';
import jwt from 'jsonwebtoken';
import { settings } from '~/panel.config'
import { environment } from "~/core/globals";
export default defineEventHandler(async (event) => {
try {
const { password } = await readBody(event);
if (!password) {
return sendError(event, createError({ statusCode: 400, message: 'password is required' }));
}
const isMatch = await bcrypt.compare(password, settings.password_hash);
if (!isMatch) {
return sendError(event, createError({ statusCode: 400, message: 'Invalid credentials!' }));
}
const token = jwt.sign({ userId: password }, environment.jwt_globals.secret!, {
expiresIn: '1h',
});
return {
message: 'Login successful!',
token
};
} catch (error) {
console.error("Login error: ", error);
return sendError(event, createError({ statusCode: 500, message: 'Internal server error' }));
}
});

View file

@ -41,7 +41,7 @@ function createEmptySettingsIfNotExists() {
} catch (error) {
console.log('Settings does not exist, creating an empty Settings-file...');
let emptySettings: SettingsJsonFile = ({
eula: false,
eula: true,
servers:[]
});
const jsonSettings = JSON.stringify(emptySettings, null, 2);