Improve signin messaging
This commit is contained in:
parent
b4e6bac566
commit
72a8ed5c16
7 changed files with 110 additions and 20 deletions
|
|
@ -1,12 +1,15 @@
|
|||
// src/api/axios.ts
|
||||
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
|
||||
|
||||
export interface AxiosErrorJSON {
|
||||
status: number;
|
||||
}
|
||||
class ApiClient {
|
||||
private client: AxiosInstance;
|
||||
|
||||
constructor() {
|
||||
this.client = axios.create({
|
||||
timeout: 60000,
|
||||
timeout: 10000,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -224,7 +224,10 @@ router.beforeEach(async (to, from) => {
|
|||
const storeSession = useSessionStore();
|
||||
try {
|
||||
if (!storeSession.isLoading && !storeSession.isAuthenticated) {
|
||||
console.log("sending to signin because we're not authenticated");
|
||||
console.log(
|
||||
"sending to signin because we're not authenticated and user wanted",
|
||||
to.fullPath,
|
||||
);
|
||||
return `/signin?next=${from.fullPath}`;
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import * as axios from "axios";
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import { SSEManager, type SSEMessage } from "@/SSEManager";
|
||||
|
|
@ -8,8 +9,20 @@ import {
|
|||
URLs,
|
||||
User,
|
||||
} from "@/type/api";
|
||||
import { apiClient } from "@/client";
|
||||
import { apiClient, AxiosErrorJSON } from "@/client";
|
||||
|
||||
export class ErrorNotSignedIn extends Error {
|
||||
constructor() {
|
||||
super("not signed in");
|
||||
this.name = "ErrorNotSignedIn";
|
||||
Object.setPrototypeOf(this, ErrorNotSignedIn.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
export interface SigninResult {
|
||||
is_success: boolean;
|
||||
status: number;
|
||||
}
|
||||
export const useSessionStore = defineStore("session", () => {
|
||||
// State
|
||||
const hasSession = ref<boolean>(false);
|
||||
|
|
@ -32,23 +45,58 @@ export const useSessionStore = defineStore("session", () => {
|
|||
});
|
||||
|
||||
// Actions
|
||||
async function doSignin(
|
||||
password: string,
|
||||
username: string,
|
||||
): Promise<SigninResult> {
|
||||
try {
|
||||
await apiClient.JSONPost("/api/signin", {
|
||||
password: password,
|
||||
username: username,
|
||||
});
|
||||
return {
|
||||
is_success: true,
|
||||
status: 200,
|
||||
};
|
||||
} catch (e: any) {
|
||||
const data: AxiosErrorJSON =
|
||||
e instanceof axios.AxiosError
|
||||
? (e.toJSON() as AxiosErrorJSON)
|
||||
: { status: 0 };
|
||||
if (!data) throw e;
|
||||
return {
|
||||
is_success: false,
|
||||
status: data.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
async function fetchSession(): Promise<Session> {
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
const data: Session = await apiClient.JSONGet("/api/session");
|
||||
isAuthenticated.value = true;
|
||||
console.log("set authenticated", isAuthenticated.value);
|
||||
console.log(
|
||||
"set authenticated",
|
||||
isAuthenticated.value,
|
||||
"due to successful GET /api/session",
|
||||
);
|
||||
impersonating.value = data.impersonating || null;
|
||||
notification_counts.value = data.notification_counts;
|
||||
organization.value = data.organization;
|
||||
self.value = data.self;
|
||||
urls.value = data.urls;
|
||||
return data;
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : "an error ocurred";
|
||||
console.error("Error fetching user:", e);
|
||||
throw new Error(error.value);
|
||||
} catch (e: any) {
|
||||
const data: AxiosErrorJSON =
|
||||
e instanceof axios.AxiosError
|
||||
? (e.toJSON() as AxiosErrorJSON)
|
||||
: { status: 0 };
|
||||
if (data.status == 401) {
|
||||
throw new ErrorNotSignedIn();
|
||||
}
|
||||
console.error("Error fetching session:", e);
|
||||
throw e;
|
||||
} finally {
|
||||
hasSession.value = true;
|
||||
isLoading.value = false;
|
||||
|
|
@ -77,7 +125,7 @@ export const useSessionStore = defineStore("session", () => {
|
|||
}
|
||||
async function signout(): Promise<void> {
|
||||
isAuthenticated.value = false;
|
||||
console.log("set authenticated", isAuthenticated.value);
|
||||
console.log("set authenticated", isAuthenticated.value, "due to signout");
|
||||
apiClient.JSONPost("/api/signout", {});
|
||||
}
|
||||
return {
|
||||
|
|
@ -93,6 +141,7 @@ export const useSessionStore = defineStore("session", () => {
|
|||
self,
|
||||
urls,
|
||||
// Actions
|
||||
doSignin,
|
||||
fetchSession,
|
||||
get,
|
||||
signout,
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@
|
|||
<a href="forgot-password.html">Forgot password?</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 text-center" v-if="isLoginSuccess">
|
||||
<div class="alert alert-success">Login successful</div>
|
||||
</div>
|
||||
<div class="mt-3 text-center" v-if="error">
|
||||
<div class="alert alert-danger">{{ error }}</div>
|
||||
</div>
|
||||
|
|
@ -113,23 +116,43 @@ import { apiClient } from "@/client";
|
|||
import ButtonLoading from "@/components/common/ButtonLoading.vue";
|
||||
import { useQueryParam } from "@/composable/use-query-param";
|
||||
import { router } from "@/route/config";
|
||||
import { useSessionStore } from "@/store/session";
|
||||
|
||||
const error = ref<string>("");
|
||||
const isLoginSuccess = ref<boolean>(false);
|
||||
const loading = ref<boolean>(false);
|
||||
const paramNext = useQueryParam("next");
|
||||
const password = ref<string>("");
|
||||
const session = useSessionStore();
|
||||
const username = ref<string>("");
|
||||
async function doLogin() {
|
||||
if (username.value == "" && password.value == "") {
|
||||
error.value =
|
||||
"Slow down there partner, you should add a username and password first.";
|
||||
return;
|
||||
} else if (username.value == "") {
|
||||
error.value = "Your username is empty - we've got to know who you are.";
|
||||
return;
|
||||
} else if (password.value == "") {
|
||||
error.value = "Your password is empty - you've got to put something there.";
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const resp = await apiClient.JSONPost("/api/signin", {
|
||||
password: password.value,
|
||||
username: username.value,
|
||||
});
|
||||
if (paramNext.value.value) {
|
||||
router.push(paramNext.value.value);
|
||||
const resp = await session.doSignin(password.value, username.value);
|
||||
isLoginSuccess.value = resp.is_success;
|
||||
if (resp.status == 200) {
|
||||
if (paramNext.value.value && paramNext.value.value != "/signin") {
|
||||
router.push(paramNext.value.value);
|
||||
} else {
|
||||
router.push("/");
|
||||
}
|
||||
} else if (resp.status == 401) {
|
||||
error.value = "Invalid credentials";
|
||||
} else {
|
||||
router.push("/");
|
||||
error.value = `Status ${resp.status}`;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("login failed", e);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue