✨ Use Vue-based login page
This commit is contained in:
68
login.html
68
login.html
@ -1,68 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Login - Timetable V2</title>
|
|
||||||
<style>
|
|
||||||
body,
|
|
||||||
html {
|
|
||||||
margin: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
background-color: #353535;
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
color: #bdbdbd;
|
|
||||||
height: 80%;
|
|
||||||
}
|
|
||||||
form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
border: 0px;
|
|
||||||
outline: none;
|
|
||||||
background-color: #3a3737;
|
|
||||||
padding: 5px 5px;
|
|
||||||
margin: 5px 0;
|
|
||||||
border: 2px solid #34631f;
|
|
||||||
border-radius: 5px;
|
|
||||||
color: #bdbdbd;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
outline: none;
|
|
||||||
padding: 5px 5px;
|
|
||||||
border: 2px solid #1f5b63;
|
|
||||||
border-radius: 5px;
|
|
||||||
color: #bdbdbd;
|
|
||||||
background-color: #242121;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<h1>Timetable V2</h1>
|
|
||||||
<form action="/login" method="POST">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
name="password"
|
|
||||||
autocomplete="current-password"
|
|
||||||
placeholder="Password"
|
|
||||||
/>
|
|
||||||
<button type="submit">Login</button>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -3,7 +3,7 @@ export class Auth {
|
|||||||
constructor() {}
|
constructor() {}
|
||||||
login = (req, res) => {
|
login = (req, res) => {
|
||||||
if (!req.body.password) {
|
if (!req.body.password) {
|
||||||
res.status(401).sendFile("login.html", { root: "../" });
|
res.redirect("/login");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (req.body.password == process.env.AUTH_PASSWORD) {
|
if (req.body.password == process.env.AUTH_PASSWORD) {
|
||||||
@ -15,18 +15,18 @@ export class Auth {
|
|||||||
});
|
});
|
||||||
res.redirect("/");
|
res.redirect("/");
|
||||||
} else {
|
} else {
|
||||||
res.status(401).sendFile("login.html", { root: "../" });
|
res.redirect("/login");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
checkLogin = (req, res, next) => {
|
checkLogin = (req, res, next) => {
|
||||||
if (!req.cookies.session) {
|
if (!req.cookies.session) {
|
||||||
res.status(401).sendFile("login.html", { root: "../" });
|
res.sendStatus(401);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (!this.activeSessions.includes(req.cookies.session)) {
|
if (!this.activeSessions.includes(req.cookies.session)) {
|
||||||
res.status(401).sendFile("login.html", { root: "../" });
|
res.sendStatus(401);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,13 +29,16 @@ const parser = new Parser(
|
|||||||
);
|
);
|
||||||
|
|
||||||
app.post("/login", auth.login);
|
app.post("/login", auth.login);
|
||||||
app.use(auth.checkLogin);
|
|
||||||
|
|
||||||
|
app.use("/api", auth.checkLogin);
|
||||||
|
app.get("/api/check", (_req, res) => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
});
|
||||||
app.get("/api/timetable", getTimetable);
|
app.get("/api/timetable", getTimetable);
|
||||||
app.get("/api/substitutions", getSubstitutions);
|
app.get("/api/substitutions", getSubstitutions);
|
||||||
app.get("/api/history", getHistory);
|
app.get("/api/history", getHistory);
|
||||||
app.get("/api/classes", getClasses);
|
app.get("/api/classes", getClasses);
|
||||||
app.get("/api/*", (req, res) => {
|
app.get("/api/*", (_req, res) => {
|
||||||
res.sendStatus(400);
|
res.sendStatus(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,9 +7,9 @@ import DateSelector from "./components/date-selector.vue";
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<TitleBar />
|
<TitleBar />
|
||||||
<DateSelector v-show="$route.name != 'Settings'" />
|
<DateSelector v-show="$route.name != 'Settings' && $route.name != 'Login'" />
|
||||||
<RouterView />
|
<RouterView />
|
||||||
<BottomNavbar />
|
<BottomNavbar v-show="$route.name != 'Login'" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -11,7 +11,7 @@ const routeName = computed(() => route.name);
|
|||||||
<div class="titlebar">
|
<div class="titlebar">
|
||||||
<span class="title">{{ routeName }}</span>
|
<span class="title">{{ routeName }}</span>
|
||||||
<div class="settings">
|
<div class="settings">
|
||||||
<RouterLink to="/settings">
|
<RouterLink to="/settings" v-show="$route.name != 'Login'">
|
||||||
<MenuIcon />
|
<MenuIcon />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,6 +3,7 @@ import TimetableView from "../views/TimetableView.vue";
|
|||||||
import SubstitutionView from "../views/SubstitutionView.vue";
|
import SubstitutionView from "../views/SubstitutionView.vue";
|
||||||
import HistoryView from "../views/HistoryView.vue";
|
import HistoryView from "../views/HistoryView.vue";
|
||||||
import SettingsView from "../views/SettingsView.vue";
|
import SettingsView from "../views/SettingsView.vue";
|
||||||
|
import LoginView from "../views/LoginView.vue";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
@ -31,6 +32,11 @@ const router = createRouter({
|
|||||||
name: "Settings",
|
name: "Settings",
|
||||||
component: SettingsView,
|
component: SettingsView,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/login",
|
||||||
|
name: "Login",
|
||||||
|
component: LoginView,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { computed } from "@vue/reactivity";
|
import { computed } from "@vue/reactivity";
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
|
import router from "./router";
|
||||||
|
|
||||||
export const substitutionFilter = ref(
|
export const substitutionFilter = ref(
|
||||||
localStorage.getItem("substitutionFilter") || "all"
|
localStorage.getItem("substitutionFilter") || "all"
|
||||||
@ -62,6 +63,10 @@ export const parsedTimetable = computed(() => {
|
|||||||
|
|
||||||
export async function fetchData() {
|
export async function fetchData() {
|
||||||
const baseUrl = "/api";
|
const baseUrl = "/api";
|
||||||
|
|
||||||
|
const checkResponse = await fetch(`${baseUrl}/check`);
|
||||||
|
if (checkResponse.status != 200) router.push("/login");
|
||||||
|
|
||||||
const timetableResponse = await fetch(
|
const timetableResponse = await fetch(
|
||||||
`${baseUrl}/timetable?class=${timetableClass.value}`
|
`${baseUrl}/timetable?class=${timetableClass.value}`
|
||||||
);
|
);
|
||||||
|
57
src/views/LoginView.vue
Normal file
57
src/views/LoginView.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<div class="login">
|
||||||
|
<h1>Timetable V2</h1>
|
||||||
|
<form action="/login" method="POST">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
autocomplete="current-password"
|
||||||
|
placeholder="Password"
|
||||||
|
/>
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login {
|
||||||
|
padding: 65px 10px 80px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #353535;
|
||||||
|
}
|
||||||
|
.login {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
color: #bdbdbd;
|
||||||
|
height: 80%;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
border: 0px;
|
||||||
|
outline: none;
|
||||||
|
background-color: #3a3737;
|
||||||
|
padding: 5px 5px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border: 2px solid #34631f;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #bdbdbd;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
outline: none;
|
||||||
|
padding: 5px 5px;
|
||||||
|
border: 2px solid #1f5b63;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #bdbdbd;
|
||||||
|
background-color: #242121;
|
||||||
|
}
|
||||||
|
</style>
|
Reference in New Issue
Block a user