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 {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
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",
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -462,6 +462,11 @@ onBeforeUnmount(()=>{
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: 'auth'
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</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) {
|
} 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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue