diff --git a/core/globals.ts b/core/globals.ts
new file mode 100644
index 0000000..7e07dd6
--- /dev/null
+++ b/core/globals.ts
@@ -0,0 +1,6 @@
+import {reactive} from "vue";
+
+
+export const jwt_globals = reactive({
+ secret: "",
+});
diff --git a/pages/index.vue b/pages/index.vue
index 42cb71e..523799a 100644
--- a/pages/index.vue
+++ b/pages/index.vue
@@ -4,10 +4,10 @@ import { reactive, ref } from 'vue';
import axios from 'axios';
import type {networkInterface} from "~/types/networkInterface";
import type {serviceInterface} from "~/types/serviceInterface";
+import {checkAuth} from "~/util/auth";
-const ignoreCache = true;
const startVm = async (vm: any) => {
@@ -195,20 +195,21 @@ const fetchSettings = async () => {
}
onMounted(async () => {
- await fetchSettings()
-
- if(settings.enable_qemu_controls) await fetchVMs()
-
- await fetchOsInfo()
- await fetchCpuTemp()
- cpuInfo.isLoaded = true
- await fetchMemoryInfo()
- await fetchNetworkInfo()
- if(settings.enable_services) await fetchServiceInfo()
- const intervalId = setInterval(fetchCpuTemp, 7000);
- onUnmounted(() => {
- clearInterval(intervalId);
- });
+ let isAuthed = await checkAuth(useRouter())
+ if(isAuthed){
+ await fetchSettings()
+ if(settings.enable_qemu_controls) await fetchVMs()
+ await fetchOsInfo()
+ await fetchCpuTemp()
+ cpuInfo.isLoaded = true
+ await fetchMemoryInfo()
+ await fetchNetworkInfo()
+ if(settings.enable_services) await fetchServiceInfo()
+ const intervalId = setInterval(fetchCpuTemp, 7000);
+ onUnmounted(() => {
+ clearInterval(intervalId);
+ });
+ }
})
diff --git a/pages/login.vue b/pages/login.vue
index 35329e3..646dd50 100644
--- a/pages/login.vue
+++ b/pages/login.vue
@@ -4,19 +4,6 @@
Login
-
-
-
Don't have an account? Register here
-
@@ -57,7 +40,24 @@ export default {
},
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);
+ }
}
}
};
diff --git a/pages/settings.vue b/pages/settings.vue
index 1261a7f..796cf8c 100644
--- a/pages/settings.vue
+++ b/pages/settings.vue
@@ -1,9 +1,9 @@
diff --git a/panel.config.ts b/panel.config.ts
index ce258f1..8c2d37c 100644
--- a/panel.config.ts
+++ b/panel.config.ts
@@ -5,7 +5,7 @@ export const settings = reactive({
//Leave empty to scan all interfaces
//or change item to "disabled" to disable interface scanning
interfaces_to_scan:[
- "eth0"
+ "enp4s0"
],
enable_qemu_controls: true,
qemu_vms: [
@@ -22,5 +22,9 @@ export const settings = reactive({
systemctl_services:[
"libvirt",
"frp"
- ]
+ ],
+ password:{
+ hash: "$2y$10$04HVBBemPypGbaMhTmUxX.DUMir1HA4hT6cst.dGabot1ZWR5IQ.6",
+ salt_rounds: 10
+ },
});
\ No newline at end of file
diff --git a/server/api/auth.ts b/server/api/auth.ts
new file mode 100644
index 0000000..235d126
--- /dev/null
+++ b/server/api/auth.ts
@@ -0,0 +1,30 @@
+import { defineEventHandler, getCookie, createError } from 'h3';
+import jwt from 'jsonwebtoken';
+import {jwt_globals} from "~/core/globals";
+
+export default defineEventHandler(async (event) => {
+ try {
+ const token = getCookie(event, 'token');
+ console.log("Checking token " + token);
+ if (!token) {
+ throw createError({ statusCode: 401, statusMessage: 'Unauthorized' });
+ }
+
+ const secret = jwt_globals.secret;
+ if (!secret) {
+ throw createError({ statusCode: 500, statusMessage: 'JWT secret not set' });
+ }
+
+ const decoded = jwt.verify(token, secret) as { userId: string };
+ if (!decoded?.userId) {
+ throw createError({ statusCode: 401, statusMessage: 'Invalid token' });
+ }
+ console.log("user has been authed, hash: " + decoded.userId);
+ return { success: true };
+ } catch (error: any) {
+ return createError({
+ statusCode: error.statusCode || 500,
+ statusMessage: error.statusMessage || 'Invalid or expired token',
+ });
+ }
+});
\ No newline at end of file
diff --git a/server/api/getNetworkInterfaces.ts b/server/api/getNetworkInterfaces.ts
index 8410a32..168a167 100644
--- a/server/api/getNetworkInterfaces.ts
+++ b/server/api/getNetworkInterfaces.ts
@@ -45,10 +45,6 @@ export default defineEventHandler(async () => {
state: network.operstate as "up" | "down" | "unknown"
})
}
-
- interfaces.forEach(obj => {
- console.log(obj.name + " is " + obj.state);
- })
return interfaces;
} catch (error) {
diff --git a/server/api/login.ts b/server/api/login.ts
new file mode 100644
index 0000000..67b6af0
--- /dev/null
+++ b/server/api/login.ts
@@ -0,0 +1,36 @@
+import bcrypt from 'bcryptjs';
+import { sendError, createError } from 'h3';
+import jwt from 'jsonwebtoken';
+import {settings} from "~/panel.config";
+import {jwt_globals} from "~/core/globals";
+
+export default defineEventHandler(async (event) => {
+ try {
+ const { password } = await readBody(event);
+
+ if (!password) {
+ console.log("password is required");
+ return sendError(event, createError({ statusCode: 400, message: 'password is required' }));
+ }
+
+
+
+ const isMatch = await bcrypt.compare(password, settings.password.hash);
+ if (!isMatch) {
+ console.log("Invalid credentials! password");
+ return sendError(event, createError({ statusCode: 400, message: 'Invalid credentials!' }));
+ }
+
+ const token = jwt.sign({ userId: password }, 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/server/plugins/init.ts b/server/plugins/init.ts
index e7c83cf..47ffa21 100644
--- a/server/plugins/init.ts
+++ b/server/plugins/init.ts
@@ -1,7 +1,13 @@
import { defineNitroPlugin } from "#imports";
-
+import { reactive } from "vue";
+import * as crypto from 'crypto';
+import {jwt_globals} from "~/core/globals";
export default defineNitroPlugin((nitroApp) => {
- console.log("Loading Server Settings");
-
+ console.log("Running init...");
+ console.log("Generating jwt secret...")
+ jwt_globals.secret = crypto.randomBytes(32).toString('base64');
+ console.log("secret: " + jwt_globals.secret)
});
+
+
diff --git a/util/auth.ts b/util/auth.ts
new file mode 100644
index 0000000..ad7b067
--- /dev/null
+++ b/util/auth.ts
@@ -0,0 +1,21 @@
+// util/auth.ts
+import axios from 'axios';
+import { useRouter } from 'vue-router'; // Import useRouter
+
+export async function checkAuth(router: any) {
+ try {
+ const response = await axios.get('/api/auth', {
+ withCredentials: true, // Ensure cookies are sent with the request
+ });
+
+ if (!response.data.success) {
+ router.push('/login');
+ }
+
+ return response.data.success;
+ } catch (error) {
+ console.log("Not authenticated, redirecting to /login");
+ router.push('/login');
+ return false;
+ }
+}