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 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,10 +195,10 @@ const fetchSettings = async () => {
|
|||
}
|
||||
|
||||
onMounted(async () => {
|
||||
let isAuthed = await checkAuth(useRouter())
|
||||
if(isAuthed){
|
||||
await fetchSettings()
|
||||
|
||||
if(settings.enable_qemu_controls) await fetchVMs()
|
||||
|
||||
await fetchOsInfo()
|
||||
await fetchCpuTemp()
|
||||
cpuInfo.isLoaded = true
|
||||
|
@ -209,6 +209,7 @@ onMounted(async () => {
|
|||
onUnmounted(() => {
|
||||
clearInterval(intervalId);
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
|
|
@ -4,19 +4,6 @@
|
|||
<h2 class="text-2xl font-bold text-center">Login</h2>
|
||||
|
||||
<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">
|
||||
<label class="label">
|
||||
<span class="label-text">Password</span>
|
||||
|
@ -34,10 +21,6 @@
|
|||
<button type="submit" class="btn btn-primary w-full">Login</button>
|
||||
</div>
|
||||
</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>
|
||||
</template>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import type { VM } from '../types/VM';
|
||||
import axios from 'axios';
|
||||
|
||||
|
||||
import {checkAuth} from "~/util/auth";
|
||||
|
||||
onMounted(async () => {
|
||||
let isAuthed = await checkAuth(useRouter())
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
|
|
@ -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
|
||||
},
|
||||
});
|
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"
|
||||
})
|
||||
}
|
||||
|
||||
interfaces.forEach(obj => {
|
||||
console.log(obj.name + " is " + obj.state);
|
||||
})
|
||||
return interfaces;
|
||||
|
||||
} 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 { 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)
|
||||
});
|
||||
|
||||
|
||||
|
|
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