feat: authentication integration

This commit is contained in:
2025-05-28 20:51:34 +02:00
parent a1ed1113d9
commit aa152a4127
26 changed files with 1371 additions and 662 deletions

136
web/src/store/auth.ts Normal file
View File

@ -0,0 +1,136 @@
import {
deleteAccount,
getAllAccounts,
updateAccountInfo,
type LocalAccount,
} from "@/repository/account";
import type { UserProfile } from "@/types";
import { create } from "zustand";
import { useDbStore } from "./db";
import { fetchProfileApi } from "@/api/profile";
export interface IAuthState {
profile: UserProfile | null;
authenticating: boolean;
activeAccount: LocalAccount | null;
loadingAccounts: boolean;
accounts: LocalAccount[];
signInRequired: boolean;
loadAccounts: () => Promise<void>;
updateActiveAccount: (account: LocalAccount) => Promise<void>;
authenticate: () => Promise<void>;
requireSignIn: () => void;
signOut: () => void;
}
const getActiveAccountId = (): string | null => {
const accountId = localStorage.getItem("guard-selected");
return accountId;
};
const saveActiveAccountId = (accountId: string): void => {
localStorage.setItem("guard-selected", accountId);
};
const resetActiveAccount = (): void => {
localStorage.removeItem("guard-selected");
};
// TODO: maybe add deleteActiveAccount
export const useAuth = create<IAuthState>((set, get) => ({
profile: null,
authenticating: false,
activeAccount: null,
accounts: [],
loadingAccounts: false,
signInRequired: false,
updateActiveAccount: async (account) => {
set({ activeAccount: account });
saveActiveAccountId(account.accountId);
set({ signInRequired: false });
},
loadAccounts: async () => {
set({ loadingAccounts: true });
const db = useDbStore.getState().db;
if (!db) return;
const accounts = await getAllAccounts(db);
console.log("loaded accounts:", accounts);
if (!accounts || accounts.length === 0) {
set({ signInRequired: true });
}
const active = getActiveAccountId();
if (!active) {
set({ signInRequired: true });
}
const account = accounts.find((acc) => acc.accountId === active);
if (!account) {
resetActiveAccount();
set({ signInRequired: true });
}
set({ activeAccount: account, accounts, loadingAccounts: false });
},
authenticate: async () => {
const { authenticating } = get();
if (authenticating) return;
set({ authenticating: true });
try {
const response = await fetchProfileApi();
console.log("authenticate response:", response);
try {
// update local account information
const db = useDbStore.getState().db;
if (db) {
await updateAccountInfo(db, {
accountId: response.id,
label: response.full_name,
...(response.profile_picture
? { profilePicture: response.profile_picture }
: {}),
});
}
} finally {
set({ profile: response });
}
} catch (err) {
// TODO: set error
console.log(err);
} finally {
set({ authenticating: false });
}
},
requireSignIn: () => set({ signInRequired: true }),
signOut: async () => {
resetActiveAccount();
const db = useDbStore.getState().db;
const activeAccount = get().activeAccount;
if (db && activeAccount) {
await deleteAccount(db, activeAccount.accountId);
}
await get().loadAccounts();
set({ activeAccount: null, signInRequired: true });
},
}));