added auth
This commit is contained in:
parent
f6b3c35b70
commit
42a77072e1
10 changed files with 145 additions and 45 deletions
6
core/globals.ts
Normal file
6
core/globals.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import {reactive} from "vue";
|
||||||
|
|
||||||
|
|
||||||
|
export const jwt_globals = reactive({
|
||||||
|
secret: "",
|
||||||
|
});
|
|
@ -4,10 +4,10 @@ import { reactive, ref } from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import type {networkInterface} from "~/types/networkInterface";
|
import type {networkInterface} from "~/types/networkInterface";
|
||||||
import type {serviceInterface} from "~/types/serviceInterface";
|
import type {serviceInterface} from "~/types/serviceInterface";
|
||||||
|
import {checkAuth} from "~/util/auth";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ignoreCache = true;
|
|
||||||
|
|
||||||
|
|
||||||
const startVm = async (vm: any) => {
|
const startVm = async (vm: any) => {
|
||||||
|
@ -195,20 +195,21 @@ const fetchSettings = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await fetchSettings()
|
let isAuthed = await checkAuth(useRouter())
|
||||||
|
if(isAuthed){
|
||||||
if(settings.enable_qemu_controls) await fetchVMs()
|
await fetchSettings()
|
||||||
|
if(settings.enable_qemu_controls) await fetchVMs()
|
||||||
await fetchOsInfo()
|
await fetchOsInfo()
|
||||||
await fetchCpuTemp()
|
await fetchCpuTemp()
|
||||||
cpuInfo.isLoaded = true
|
cpuInfo.isLoaded = true
|
||||||
await fetchMemoryInfo()
|
await fetchMemoryInfo()
|
||||||
await fetchNetworkInfo()
|
await fetchNetworkInfo()
|
||||||
if(settings.enable_services) await fetchServiceInfo()
|
if(settings.enable_services) await fetchServiceInfo()
|
||||||
const intervalId = setInterval(fetchCpuTemp, 7000);
|
const intervalId = setInterval(fetchCpuTemp, 7000);
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,19 +4,6 @@
|
||||||
<h2 class="text-2xl font-bold text-center">Login</h2>
|
<h2 class="text-2xl font-bold text-center">Login</h2>
|
||||||
|
|
||||||
<form @submit.prevent="handleLogin" class="mt-4">
|
<form @submit.prevent="handleLogin" class="mt-4">
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">E-Mail</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
v-model="email"
|
|
||||||
type="email"
|
|
||||||
placeholder="Enter your email"
|
|
||||||
class="input input-bordered"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-control mt-2">
|
<div class="form-control mt-2">
|
||||||
<label class="label">
|
<label class="label">
|
||||||
<span class="label-text">Password</span>
|
<span class="label-text">Password</span>
|
||||||
|
@ -34,10 +21,6 @@
|
||||||
<button type="submit" class="btn btn-primary w-full">Login</button>
|
<button type="submit" class="btn btn-primary w-full">Login</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="mt-4 text-center text-sm">
|
|
||||||
<p>Don't have an account? <a href="/register" class="text-blue-500 hover:underline">Register here</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -57,7 +40,24 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async handleLogin() {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { VM } from '../types/VM';
|
import {checkAuth} from "~/util/auth";
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
let isAuthed = await checkAuth(useRouter())
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ export const settings = reactive({
|
||||||
//Leave empty to scan all interfaces
|
//Leave empty to scan all interfaces
|
||||||
//or change item to "disabled" to disable interface scanning
|
//or change item to "disabled" to disable interface scanning
|
||||||
interfaces_to_scan:[
|
interfaces_to_scan:[
|
||||||
"eth0"
|
"enp4s0"
|
||||||
],
|
],
|
||||||
enable_qemu_controls: true,
|
enable_qemu_controls: true,
|
||||||
qemu_vms: [
|
qemu_vms: [
|
||||||
|
@ -22,5 +22,9 @@ export const settings = reactive({
|
||||||
systemctl_services:[
|
systemctl_services:[
|
||||||
"libvirt",
|
"libvirt",
|
||||||
"frp"
|
"frp"
|
||||||
]
|
],
|
||||||
|
password:{
|
||||||
|
hash: "$2y$10$04HVBBemPypGbaMhTmUxX.DUMir1HA4hT6cst.dGabot1ZWR5IQ.6",
|
||||||
|
salt_rounds: 10
|
||||||
|
},
|
||||||
});
|
});
|
30
server/api/auth.ts
Normal file
30
server/api/auth.ts
Normal file
|
@ -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',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -45,10 +45,6 @@ export default defineEventHandler(async () => {
|
||||||
state: network.operstate as "up" | "down" | "unknown"
|
state: network.operstate as "up" | "down" | "unknown"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
interfaces.forEach(obj => {
|
|
||||||
console.log(obj.name + " is " + obj.state);
|
|
||||||
})
|
|
||||||
return interfaces;
|
return interfaces;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
36
server/api/login.ts
Normal file
36
server/api/login.ts
Normal file
|
@ -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' }));
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,7 +1,13 @@
|
||||||
import { defineNitroPlugin } from "#imports";
|
import { defineNitroPlugin } from "#imports";
|
||||||
|
import { reactive } from "vue";
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import {jwt_globals} from "~/core/globals";
|
||||||
|
|
||||||
export default defineNitroPlugin((nitroApp) => {
|
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)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
21
util/auth.ts
Normal file
21
util/auth.ts
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue