chore: Typescript Auth (#20470)

This commit is contained in:
Maschga 2025-04-09 22:57:56 +02:00 • committed by GitHub
parent 1e1e7947b1
commit 2cabe49239
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 133 additions and 123 deletions

View file

@ -1,5 +1,5 @@
import axios from "axios";
import { openLoginModal } from "./components/Auth/auth.js";
import { openLoginModal } from "./components/Auth/auth";
const { protocol, hostname, port, pathname } = window.location;

View file

@ -57,14 +57,15 @@
</GenericModal>
</template>
<script>
<script lang="ts">
import GenericModal from "../Helper/GenericModal.vue";
import Modal from "bootstrap/js/dist/modal";
import api from "../../api.js";
import { updateAuthStatus, getAndClearNextUrl, getAndClearNextModal, isLoggedIn } from "./auth.js";
import { updateAuthStatus, getAndClearNextUrl, getAndClearNextModal, isLoggedIn } from "./auth";
import { docsPrefix } from "../../i18n.js";
import { defineComponent } from "vue";
export default {
export default defineComponent({
name: "LoginModal",
components: { GenericModal },
data: () => {
@ -98,11 +99,11 @@ export default {
this.iframeHint = false;
},
focus() {
console.log(this.$refs.password);
this.$refs.password.focus();
console.log(this.$refs["password"]);
this.$refs["password"]?.focus();
},
closeModal() {
Modal.getOrCreateInstance(document.getElementById("loginModal")).hide();
Modal.getOrCreateInstance(document.getElementById("loginModal") as HTMLElement).hide();
},
async login() {
this.loading = true;
@ -136,13 +137,15 @@ export default {
}
} catch (err) {
console.error(err);
this.error = err.message;
if (err instanceof Error) {
this.error = err.message;
}
} finally {
this.loading = false;
}
},
},
};
});
</script>
<style scoped>
form {

View file

@ -85,13 +85,14 @@
</GenericModal>
</template>
<script type="js">
<script type="ts">
import GenericModal from "../Helper/GenericModal.vue";
import FormRow from "../Helper/FormRow.vue";
import api from "../../api.js";
import { updateAuthStatus, isConfigured } from "./auth.js";
import { updateAuthStatus, isConfigured } from "./auth";
import { defineComponent } from "vue";
export default {
export default defineComponent({
name: "PasswordModal",
components: { FormRow, GenericModal },
data: () => {
@ -173,5 +174,5 @@ export default {
}
},
},
};
});
</script>

View file

@ -1,107 +0,0 @@
import { reactive, watch } from "vue";
import api from "../../api.js";
import store from "../../store.js";
import Modal from "bootstrap/js/dist/modal";
import { isSystemError } from "../../utils/fatal.js";
const auth = reactive({
configured: true,
loggedIn: null, // true / false / null (unknown)
nextUrl: null, // url to navigate to after login
nextModal: null, // modal instance to show after login
});
export async function updateAuthStatus() {
if (store.state.offline || isSystemError(store.state.fatal)) {
// system not ready, skip auth check
return;
}
try {
const res = await api.get("/auth/status", {
validateStatus: (code) => [200, 501, 500].includes(code),
});
if (res.status === 501) {
auth.configured = false;
}
if (res.status === 200) {
auth.configured = true;
auth.loggedIn = res.data === true;
}
if (res.status === 500) {
auth.loggedIn = null;
console.log("unable to fetch auth status", res);
}
} catch (e) {
console.log("unable to fetch auth status", e);
}
}
export async function logout() {
try {
await api.post("/auth/logout");
await updateAuthStatus();
auth.nextUrl = null;
} catch (e) {
console.log("unable to logout", e);
}
}
export function isLoggedIn() {
return auth.loggedIn === true;
}
export function statusUnknown() {
return auth.loggedIn === null;
}
export function isConfigured() {
return auth.configured;
}
export function getAndClearNextUrl() {
const nextUrl = auth.nextUrl;
auth.nextUrl = null;
return nextUrl;
}
export function getAndClearNextModal() {
const nextModal = auth.nextModal;
auth.nextModal = null;
return nextModal;
}
export function openLoginModal(nextUrl = null, nextModal = null) {
auth.nextUrl = nextUrl;
auth.nextModal = nextModal;
const modal = Modal.getOrCreateInstance(document.getElementById("loginModal"));
modal.show();
}
// show/hide password modal based on auth status
watch(
() => auth.configured,
(configured) => {
console.log("configured", configured);
const modal = Modal.getOrCreateInstance(document.getElementById("passwordModal"));
if (configured) {
modal.hide();
} else {
modal.show();
}
}
);
let timeoutId = null;
function debounedUpdateAuthStatus() {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
updateAuthStatus();
}, 500);
}
// update auth status on reconnect or server restart
watch(() => store.state.offline, debounedUpdateAuthStatus);
watch(() => store.state.startup, debounedUpdateAuthStatus);
export default auth;

View file

@ -0,0 +1,109 @@
import { reactive, watch } from "vue";
import api from "../../api.js";
import store from "../../store.js";
import Modal from "bootstrap/js/dist/modal";
import { isSystemError } from "../../utils/fatal.js";
const auth = reactive({
configured: true,
loggedIn: null as boolean | null, // true / false / null (unknown)
nextUrl: null as string | null, // url to navigate to after login
nextModal: null as Modal | null, // modal instance to show after login
});
export async function updateAuthStatus() {
if (store.state.offline || isSystemError(store.state.fatal)) {
// system not ready, skip auth check
return;
}
try {
const res = await api.get("/auth/status", {
validateStatus: (code) => [200, 501, 500].includes(code),
});
if (res.status === 501) {
auth.configured = false;
}
if (res.status === 200) {
auth.configured = true;
auth.loggedIn = res.data === true;
}
if (res.status === 500) {
auth.loggedIn = null;
console.log("unable to fetch auth status", res);
}
} catch (e) {
console.log("unable to fetch auth status", e);
}
}
export async function logout() {
try {
await api.post("/auth/logout");
await updateAuthStatus();
auth.nextUrl = null;
} catch (e) {
console.log("unable to logout", e);
}
}
export function isLoggedIn() {
return auth.loggedIn === true;
}
export function statusUnknown() {
return auth.loggedIn === null;
}
export function isConfigured() {
return auth.configured;
}
export function getAndClearNextUrl() {
const nextUrl = auth.nextUrl;
auth.nextUrl = null;
return nextUrl;
}
export function getAndClearNextModal() {
const nextModal = auth.nextModal;
auth.nextModal = null;
return nextModal;
}
export function openLoginModal(nextUrl = null, nextModal = null) {
auth.nextUrl = nextUrl;
auth.nextModal = nextModal;
const modal = Modal.getOrCreateInstance(document.getElementById("loginModal") as HTMLElement);
modal.show();
}
// show/hide password modal based on auth status
watch(
() => auth.configured,
(configured) => {
console.log("configured", configured);
const modal = Modal.getOrCreateInstance(
document.getElementById("passwordModal") as HTMLElement
);
if (configured) {
modal.hide();
} else {
modal.show();
}
}
);
let timeoutId: number | undefined = undefined;
function debounedUpdateAuthStatus() {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
updateAuthStatus();
}, 500);
}
// update auth status on reconnect or server restart
watch(() => store.state.offline, debounedUpdateAuthStatus);
watch(() => store.state.startup, debounedUpdateAuthStatus);
export default auth;

View file

@ -140,7 +140,7 @@
import Modal from "bootstrap/js/dist/modal";
import { docsPrefix } from "../i18n.js";
import { performRestart } from "../restart.js";
import { isLoggedIn, openLoginModal } from "./Auth/auth.js";
import { isLoggedIn, openLoginModal } from "./Auth/auth";
export default {
name: "HelpModal",

View file

@ -131,7 +131,7 @@ import "@h2d2/shopicons/es/regular/moonstars";
import "@h2d2/shopicons/es/regular/menu";
import "@h2d2/shopicons/es/regular/newtab";
import collector from "../../mixins/collector.js";
import { logout, isLoggedIn, openLoginModal } from "../Auth/auth.js";
import { logout, isLoggedIn, openLoginModal } from "../Auth/auth";
import baseAPI from "./baseapi.js";
import { isApp, sendToApp } from "../../utils/native.js";
import { isUserConfigError } from "../../utils/fatal.js";

View file

@ -7,7 +7,7 @@ import {
updateAuthStatus,
isLoggedIn,
isConfigured,
} from "./components/Auth/auth.js";
} from "./components/Auth/auth";
function hideAllModals() {
[...document.querySelectorAll(".modal.show")].forEach((modal) => {

View file

@ -8,9 +8,13 @@ declare global {
export interface State {
offline: boolean;
startup?: boolean;
loadpoints: [];
forecast?: any;
currency?: CURRENCY;
fatal?: {
error: any;
};
}
export enum CURRENCY {