diff --git a/.password_hash_gen/generator.js b/.password_hash_gen/generator.js new file mode 100644 index 0000000..18624e8 --- /dev/null +++ b/.password_hash_gen/generator.js @@ -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); + }); diff --git a/.password_hash_gen/package.json b/.password_hash_gen/package.json new file mode 100644 index 0000000..868ee04 --- /dev/null +++ b/.password_hash_gen/package.json @@ -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": "" +} diff --git a/core/globals.ts b/core/globals.ts index 646c532..696b2f6 100644 --- a/core/globals.ts +++ b/core/globals.ts @@ -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; } diff --git a/middleware/auth.ts b/middleware/auth.ts new file mode 100644 index 0000000..c8b6cf6 --- /dev/null +++ b/middleware/auth.ts @@ -0,0 +1,9 @@ +import axios from "axios"; + + +export default defineNuxtRouteMiddleware(async (to, from) => { + const user = useCookie('token') + if (!user.value) { + return navigateTo('/login') + } +}) diff --git a/package.json b/package.json index ca55278..6a4a0e6 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/pages/index.vue b/pages/index.vue index 415016d..7f08812 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -462,6 +462,11 @@ onBeforeUnmount(()=>{ }) +definePageMeta({ + middleware: 'auth' +}) + + diff --git a/pages/login.vue b/pages/login.vue new file mode 100644 index 0000000..646dd50 --- /dev/null +++ b/pages/login.vue @@ -0,0 +1,65 @@ + + + \ No newline at end of file diff --git a/panel.config.ts b/panel.config.ts new file mode 100644 index 0000000..21ee369 --- /dev/null +++ b/panel.config.ts @@ -0,0 +1,5 @@ +import { reactive } from "vue"; + +export const settings = reactive({ + password_hash: "" +}); \ No newline at end of file diff --git a/server/api/login.ts b/server/api/login.ts new file mode 100644 index 0000000..6c21fa0 --- /dev/null +++ b/server/api/login.ts @@ -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' })); + } +}); diff --git a/util/jsonLoader.ts b/util/jsonLoader.ts index 028047e..710b380 100644 --- a/util/jsonLoader.ts +++ b/util/jsonLoader.ts @@ -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);