Skip to content

Commit 5419cc6

Browse files
authored
Merge branch 'evcc-io:master' into master
2 parents 3cf7898 + dc13701 commit 5419cc6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1247
-63
lines changed

assets/js/api.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import axios from "axios";
2+
import { openLoginModal } from "./auth";
23

34
const { protocol, hostname, port, pathname } = window.location;
45

@@ -15,13 +16,19 @@ const api = axios.create({
1516
api.interceptors.response.use(
1617
(response) => response,
1718
(error) => {
19+
// handle unauthorized errors
20+
if (error.response?.status === 401) {
21+
openLoginModal();
22+
return Promise.reject(error);
23+
}
24+
1825
const message = [`${error.message}.`];
1926
if (error.response?.data?.error) {
2027
message.push(`${error.response.data.error}.`);
2128
}
2229
if (error.config) {
2330
const method = error.config.method.toUpperCase();
24-
const url = error.config.baseURL + error.config.url;
31+
const url = error.request.responseURL;
2532
message.push(`${method} ${url}`);
2633
}
2734
window.app.raise({ message });

assets/js/auth.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { reactive, watch } from "vue";
2+
import api from "./api";
3+
import Modal from "bootstrap/js/dist/modal";
4+
5+
const auth = reactive({
6+
configured: true,
7+
loggedIn: false,
8+
});
9+
10+
export async function updateAuthStatus() {
11+
try {
12+
const res = await api.get("/auth/status", {
13+
validateStatus: (code) => [200, 501].includes(code),
14+
});
15+
if (res.status === 501) {
16+
auth.configured = false;
17+
}
18+
if (res.status === 200) {
19+
auth.configured = true;
20+
auth.loggedIn = res.data === true;
21+
}
22+
} catch (e) {
23+
console.log("unable to fetch auth status", e);
24+
}
25+
}
26+
27+
export async function logout() {
28+
try {
29+
await api.post("/auth/logout");
30+
await updateAuthStatus();
31+
} catch (e) {
32+
console.log("unable to logout", e);
33+
}
34+
}
35+
36+
export function isLoggedIn() {
37+
return auth.loggedIn;
38+
}
39+
40+
export function isConfigured() {
41+
return auth.configured;
42+
}
43+
44+
export function openLoginModal() {
45+
const modal = Modal.getOrCreateInstance(document.getElementById("loginModal"));
46+
modal.show();
47+
}
48+
49+
// show/hide password modal based on auth status
50+
watch(
51+
() => auth.configured,
52+
(configured) => {
53+
const modal = Modal.getOrCreateInstance(document.getElementById("passwordModal"));
54+
configured ? modal.hide() : modal.show();
55+
}
56+
);
57+
58+
export default auth;

assets/js/components/Config/GeneralConfig.vue

+8-2
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,17 @@
2727
</a>
2828
</dd>
2929
</dl>
30-
<dl class="row wip">
30+
<dl class="row wip" data-testid="generalconfig-password">
3131
<dt class="col-sm-4">Password</dt>
3232
<dd class="col-sm-8">
3333
*******
34-
<a href="#" class="ms-2 d-inline-block text-muted" @click.prevent="todo">edit</a>
34+
<!-- TODO: update this once auth is released -->
35+
<a
36+
href="#"
37+
class="ms-2 d-inline-block text-muted"
38+
@click.prevent="todo() /* openModal('passwordModal')*/"
39+
>edit</a
40+
>
3541
</dd>
3642
</dl>
3743
<dl class="row wip">

assets/js/components/GenericModal.vue

+20-15
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
:id="id"
55
ref="modal"
66
class="modal fade text-dark"
7-
data-bs-backdrop="true"
7+
:class="sizeClass"
88
tabindex="-1"
99
role="dialog"
1010
aria-hidden="true"
11+
:data-bs-backdrop="uncloseable ? 'static' : 'true'"
12+
:data-bs-keyboard="uncloseable ? 'false' : 'true'"
1113
:data-testid="dataTestid"
1214
>
1315
<div class="modal-dialog modal-dialog-centered" role="document">
@@ -17,6 +19,7 @@
1719
{{ title }}
1820
</h5>
1921
<button
22+
v-if="!uncloseable"
2023
type="button"
2124
class="btn-close"
2225
data-bs-dismiss="modal"
@@ -41,27 +44,29 @@ export default {
4144
id: String,
4245
title: String,
4346
dataTestid: String,
47+
uncloseable: Boolean,
48+
size: String,
4449
},
45-
emits: ["visibilityChanged"],
46-
watch: {
47-
isModalVisible(visible) {
48-
this.$emit("changed", visible);
49-
},
50-
},
50+
emits: ["open", "closed"],
5151
mounted() {
52-
this.$refs.modal.addEventListener("show.bs.modal", this.modalVisible);
53-
this.$refs.modal.addEventListener("hide.bs.modal", this.modalInvisible);
52+
this.$refs.modal.addEventListener("show.bs.modal", this.handleShow);
53+
this.$refs.modal.addEventListener("hidden.bs.modal", this.handleHidden);
5454
},
5555
unmounted() {
56-
this.$refs.modal?.removeEventListener("show.bs.modal", this.modalVisible);
57-
this.$refs.modal?.removeEventListener("hide.bs.modal", this.modalInvisible);
56+
this.$refs.modal?.removeEventListener("show.bs.modal", this.handleShow);
57+
this.$refs.modal?.removeEventListener("hidden.bs.modal", this.handleHidden);
58+
},
59+
computed: {
60+
sizeClass() {
61+
return this.size ? `modal-${this.size}` : "";
62+
},
5863
},
5964
methods: {
60-
modalVisible() {
61-
this.isModalVisible = true;
65+
handleShow() {
66+
this.$emit("open");
6267
},
63-
modalInvisible() {
64-
this.isModalVisible = false;
68+
handleHidden() {
69+
this.$emit("closed");
6570
},
6671
open() {
6772
Modal.getOrCreateInstance(this.$refs.modal).show();

assets/js/components/HelpModal.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
<div class="modal-footer d-flex justify-content-between">
106106
<button
107107
type="button"
108-
class="btn btn-outline-secondary"
108+
class="btn btn-link text-muted"
109109
data-bs-dismiss="modal"
110110
@click="openHelpModal"
111111
>

assets/js/components/LoginModal.vue

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<template>
2+
<GenericModal
3+
id="loginModal"
4+
:title="$t('loginModal.title')"
5+
size="sm"
6+
data-testid="login-modal"
7+
@open="open"
8+
@closed="closed"
9+
>
10+
<form v-if="modalVisible" @submit.prevent="login">
11+
<p v-if="error" class="text-danger">{{ $t("loginModal.error") }}{{ error }}</p>
12+
13+
<div class="mb-4">
14+
<label for="loginPassword" class="col-form-label">
15+
<div class="w-100">
16+
<span class="label">{{ $t("loginModal.password") }}</span>
17+
</div>
18+
</label>
19+
<input
20+
id="loginPassword"
21+
v-model="password"
22+
ref="password"
23+
class="form-control"
24+
autocomplete="current-password"
25+
type="password"
26+
required
27+
/>
28+
</div>
29+
30+
<button type="submit" class="btn btn-primary w-100 mb-3" :disabled="loading">
31+
{{ $t("loginModal.login") }}
32+
</button>
33+
</form>
34+
</GenericModal>
35+
</template>
36+
37+
<script>
38+
import GenericModal from "./GenericModal.vue";
39+
import Modal from "bootstrap/js/dist/modal";
40+
import api from "../api";
41+
import { updateAuthStatus } from "../auth";
42+
43+
export default {
44+
name: "LoginModal",
45+
components: { GenericModal },
46+
data: () => {
47+
return {
48+
modalVisible: false,
49+
password: "",
50+
loading: false,
51+
error: "",
52+
};
53+
},
54+
methods: {
55+
open() {
56+
this.modalVisible = true;
57+
},
58+
closed() {
59+
this.modalVisible = false;
60+
this.password = "";
61+
this.loading = false;
62+
this.error = "";
63+
},
64+
focus() {
65+
console.log(this.$refs.password);
66+
this.$refs.password.focus();
67+
},
68+
closeModal() {
69+
Modal.getOrCreateInstance(document.getElementById("loginModal")).hide();
70+
},
71+
async login() {
72+
this.loading = true;
73+
this.error = "";
74+
75+
try {
76+
const data = { password: this.password };
77+
const res = await api.post("/auth/login", data, {
78+
validateStatus: (code) => [200, 401].includes(code),
79+
});
80+
if (res.status === 200) {
81+
this.closeModal();
82+
await updateAuthStatus();
83+
this.$router.push({ path: "/config" });
84+
this.password = "";
85+
}
86+
if (res.status === 401) {
87+
this.error = this.$t("loginModal.invalid");
88+
}
89+
} catch (err) {
90+
console.error(err);
91+
this.error = err.message;
92+
} finally {
93+
this.loading = false;
94+
}
95+
},
96+
},
97+
};
98+
</script>

0 commit comments

Comments
 (0)