Mercury/ui/src/stores/auth.ts
Matthew L McPeak ebeba5fe29
Some checks failed
ci / test (push) Failing after 6s
ci / build-ui (push) Successful in 12s
ci / publish (push) Has been skipped
Initial Commit
2026-06-17 21:34:16 -04:00

70 lines
2 KiB
TypeScript

import { defineStore } from "pinia";
import { ref, computed } from "vue";
interface Claims {
sub: string;
permissions: string;
exp: number;
}
function parseJwt(token: string): Claims | null {
try {
const payload = token.split(".")[1];
return payload ? (JSON.parse(atob(payload)) as Claims) : null;
} catch {
return null;
}
}
export const useAuthStore = defineStore("auth", () => {
const token = ref<string | null>(localStorage.getItem("mercury_token"));
const claims = computed<Claims | null>(() =>
token.value ? parseJwt(token.value) : null,
);
const isAuthenticated = computed(() => {
if (!claims.value) return false;
return claims.value.exp * 1000 > Date.now();
});
const username = computed(() => claims.value?.sub ?? "");
function hasPermission(bit: bigint): boolean {
if (!claims.value) return false;
const mask = BigInt(claims.value.permissions);
return (mask & bit) !== 0n;
}
const isSuperAdmin = computed(() => hasPermission(32n));
async function login(username: string, password: string): Promise<void> {
const res = await fetch("/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});
if (!res.ok) throw new Error("Invalid credentials");
const data = await res.json();
token.value = data.token;
localStorage.setItem("mercury_token", data.token);
}
function logout() {
token.value = null;
localStorage.removeItem("mercury_token");
}
function authHeaders(): Record<string, string> {
return token.value ? { Authorization: `Bearer ${token.value}` } : {};
}
return {
token,
claims,
isAuthenticated,
username,
isSuperAdmin,
hasPermission,
login,
logout,
authHeaders,
};
});