added authentication
This commit is contained in:
parent
0504c7f8c5
commit
30c00b46df
10 changed files with 180 additions and 7 deletions
42
.password_hash_gen/generator.js
Normal file
42
.password_hash_gen/generator.js
Normal 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);
|
||||
});
|
11
.password_hash_gen/package.json
Normal file
11
.password_hash_gen/package.json
Normal 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": ""
|
||||
}
|
|
@ -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
9
middleware/auth.ts
Normal 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')
|
||||
}
|
||||
})
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -462,6 +462,11 @@ onBeforeUnmount(()=>{
|
|||
})
|
||||
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth'
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
|
65
pages/login.vue
Normal file
65
pages/login.vue
Normal 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
5
panel.config.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { reactive } from "vue";
|
||||
|
||||
export const settings = reactive({
|
||||
password_hash: ""
|
||||
});
|
32
server/api/login.ts
Normal file
32
server/api/login.ts
Normal 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' }));
|
||||
}
|
||||
});
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue