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 {MinecraftServer} from "~/types/MinecraftServer";
import type {SettingsJsonFile} from "~/types/SettingsJsonFile"; import type {SettingsJsonFile} from "~/types/SettingsJsonFile";
import * as os from 'os'; import * as os from 'os';
import { reactive } from "vue";
import {saveJsonFile} from "~/util/jsonLoader"; import {saveJsonFile} from "~/util/jsonLoader";
export namespace environment{ export namespace environment{
export const jwt_globals = reactive({
secret: "tH5kunVlY4rDIv10Y6Hy0NoLKpH1uYtW",
});
export namespace paths{ export namespace paths{
export let data: string = `${os.homedir()}/.mcservermanager`; export let data: string = `${os.homedir()}/.mcservermanager`;
export let servers: string = `${os.homedir()}/.mcservermanager/servers`; export let servers: string = `${os.homedir()}/.mcservermanager/servers`;
} }
export namespace files{ export namespace files{
export let settings: string = paths.data + "/settings.json"; export let settings: string = paths.data + "/settings.json";
} }
export let settings: SettingsJsonFile; 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", "dev": "nuxt dev",
"generate": "nuxt generate", "generate": "nuxt generate",
"preview": "nuxt preview", "preview": "nuxt preview",
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare",
"password_gen": "node .password_hash_gen/generator.js"
}, },
"dependencies": { "dependencies": {
"@nuxt/icon": "^1.10.3", "@nuxt/icon": "^1.10.3",
"@nuxtjs/tailwindcss": "^6.13.1", "@nuxtjs/tailwindcss": "^6.13.1",
"axios": "^1.7.9", "axios": "^1.7.9",
"bcryptjs": "^3.0.2",
"daisyui": "^4.12.23", "daisyui": "^4.12.23",
"execa": "^9.5.2", "execa": "^9.5.2",
"ini": "^5.0.0", "ini": "^5.0.0",
"jsonwebtoken": "^9.0.2",
"nuxt": "^3.15.4", "nuxt": "^3.15.4",
"vue": "latest", "vue": "latest",
"vue-router": "latest" "vue-router": "latest"
}, },
"devDependencies": { "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> </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) { } catch (error) {
console.log('Settings does not exist, creating an empty Settings-file...'); console.log('Settings does not exist, creating an empty Settings-file...');
let emptySettings: SettingsJsonFile = ({ let emptySettings: SettingsJsonFile = ({
eula: false, eula: true,
servers:[] servers:[]
}); });
const jsonSettings = JSON.stringify(emptySettings, null, 2); const jsonSettings = JSON.stringify(emptySettings, null, 2);