chore(frontend): introduce prettier + eslint and prune unused code
- Add Prettier 3 with prettier-plugin-organize-imports (sorts/removes unused imports) - Add ESLint 10 flat config with typescript-eslint + eslint-plugin-vue + eslint-config-prettier - Add root scripts: format, format:check, lint, lint:fix - Reformat 257 files across admin-web, ops-web, desktop-client, packages - Remove unused locals/exports flagged by --noUnusedLocals/--noUnusedParameters - Fix duplicate localTabLabel key, surrogate-pair regex u-flag, NBSP literal in regex - Skip server/ (Go) and apps/browser-extension/ (deprecated per ADR) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { computed, ref } from "vue";
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import type { LoginRequest, UserInfo } from "@geo/shared-types";
|
||||
import type { LoginRequest, UserInfo } from '@geo/shared-types'
|
||||
|
||||
import {
|
||||
authApi,
|
||||
isAuthSessionExpiredError,
|
||||
refreshStoredAuthSession,
|
||||
shouldRefreshAccessToken,
|
||||
} from "@/lib/api";
|
||||
} from '@/lib/api'
|
||||
import {
|
||||
clearStoredSession,
|
||||
readStoredSession,
|
||||
@@ -16,175 +16,181 @@ import {
|
||||
setStoredUser,
|
||||
subscribeStoredSession,
|
||||
type SessionSnapshot,
|
||||
} from "@/lib/session";
|
||||
} from '@/lib/session'
|
||||
|
||||
export const useAuthStore = defineStore("auth", () => {
|
||||
const accessToken = ref<string | null>(null);
|
||||
const refreshToken = ref<string | null>(null);
|
||||
const accessTokenExpiresAt = ref<number | null>(null);
|
||||
const user = ref<UserInfo | null>(null);
|
||||
const initialized = ref(false);
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
let refreshTimer: number | null = null;
|
||||
let lifecycleRefreshRegistered = false;
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
const accessToken = ref<string | null>(null)
|
||||
const refreshToken = ref<string | null>(null)
|
||||
const accessTokenExpiresAt = ref<number | null>(null)
|
||||
const user = ref<UserInfo | null>(null)
|
||||
const initialized = ref(false)
|
||||
let unsubscribe: (() => void) | null = null
|
||||
let refreshTimer: number | null = null
|
||||
let lifecycleRefreshRegistered = false
|
||||
|
||||
function clearRefreshTimer(): void {
|
||||
if (refreshTimer) {
|
||||
window.clearTimeout(refreshTimer);
|
||||
refreshTimer = null;
|
||||
window.clearTimeout(refreshTimer)
|
||||
refreshTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleRefresh(): void {
|
||||
clearRefreshTimer();
|
||||
if (!refreshToken.value || typeof window === "undefined") {
|
||||
return;
|
||||
clearRefreshTimer()
|
||||
if (!refreshToken.value || typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
const nextRefreshAt = accessTokenExpiresAt.value
|
||||
? accessTokenExpiresAt.value * 1000 - 2 * 60 * 1000
|
||||
: Date.now() + 30 * 1000;
|
||||
const delay = Math.max(30 * 1000, nextRefreshAt - Date.now());
|
||||
: Date.now() + 30 * 1000
|
||||
const delay = Math.max(30 * 1000, nextRefreshAt - Date.now())
|
||||
|
||||
refreshTimer = window.setTimeout(() => {
|
||||
void refreshSession();
|
||||
}, delay);
|
||||
void refreshSession()
|
||||
}, delay)
|
||||
}
|
||||
|
||||
function sync(snapshot: SessionSnapshot = readStoredSession()): void {
|
||||
accessToken.value = snapshot.accessToken;
|
||||
refreshToken.value = snapshot.refreshToken;
|
||||
accessTokenExpiresAt.value = snapshot.accessTokenExpiresAt;
|
||||
user.value = snapshot.user;
|
||||
scheduleRefresh();
|
||||
accessToken.value = snapshot.accessToken
|
||||
refreshToken.value = snapshot.refreshToken
|
||||
accessTokenExpiresAt.value = snapshot.accessTokenExpiresAt
|
||||
user.value = snapshot.user
|
||||
scheduleRefresh()
|
||||
}
|
||||
|
||||
async function refreshSession(): Promise<void> {
|
||||
if (!refreshToken.value) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await refreshStoredAuthSession(refreshToken.value);
|
||||
await refreshStoredAuthSession(refreshToken.value)
|
||||
} catch (error) {
|
||||
if (isAuthSessionExpiredError(error)) {
|
||||
clearStoredSession({ notifyAuthExpired: true });
|
||||
return;
|
||||
clearStoredSession({ notifyAuthExpired: true })
|
||||
return
|
||||
}
|
||||
|
||||
scheduleRefreshRetry();
|
||||
scheduleRefreshRetry()
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleRefreshRetry(): void {
|
||||
clearRefreshTimer();
|
||||
if (!refreshToken.value || typeof window === "undefined") {
|
||||
return;
|
||||
clearRefreshTimer()
|
||||
if (!refreshToken.value || typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
refreshTimer = window.setTimeout(() => {
|
||||
void refreshSession();
|
||||
}, 60 * 1000);
|
||||
void refreshSession()
|
||||
}, 60 * 1000)
|
||||
}
|
||||
|
||||
async function refreshSessionIfNeeded(minRemainingMs = 2 * 60 * 1000): Promise<void> {
|
||||
if (!refreshToken.value) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (!accessToken.value || shouldRefreshAccessToken(accessTokenExpiresAt.value, minRemainingMs)) {
|
||||
await refreshSession();
|
||||
if (
|
||||
!accessToken.value ||
|
||||
shouldRefreshAccessToken(accessTokenExpiresAt.value, minRemainingMs)
|
||||
) {
|
||||
await refreshSession()
|
||||
}
|
||||
}
|
||||
|
||||
function registerLifecycleRefresh(): void {
|
||||
if (lifecycleRefreshRegistered || typeof window === "undefined") {
|
||||
return;
|
||||
if (lifecycleRefreshRegistered || typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
lifecycleRefreshRegistered = true;
|
||||
window.addEventListener("focus", () => {
|
||||
void refreshSessionIfNeeded();
|
||||
});
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState === "visible") {
|
||||
void refreshSessionIfNeeded();
|
||||
lifecycleRefreshRegistered = true
|
||||
window.addEventListener('focus', () => {
|
||||
void refreshSessionIfNeeded()
|
||||
})
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
void refreshSessionIfNeeded()
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
async function bootstrap(): Promise<void> {
|
||||
if (!unsubscribe) {
|
||||
unsubscribe = subscribeStoredSession(sync);
|
||||
unsubscribe = subscribeStoredSession(sync)
|
||||
}
|
||||
|
||||
sync();
|
||||
registerLifecycleRefresh();
|
||||
sync()
|
||||
registerLifecycleRefresh()
|
||||
|
||||
try {
|
||||
if (refreshToken.value) {
|
||||
await refreshSessionIfNeeded(Number.POSITIVE_INFINITY);
|
||||
await refreshSessionIfNeeded(Number.POSITIVE_INFINITY)
|
||||
}
|
||||
|
||||
if (accessToken.value) {
|
||||
const currentUser = await authApi.me();
|
||||
setStoredUser(currentUser);
|
||||
const currentUser = await authApi.me()
|
||||
setStoredUser(currentUser)
|
||||
}
|
||||
} catch (error) {
|
||||
if (isAuthSessionExpiredError(error)) {
|
||||
clearStoredSession({ notifyAuthExpired: true });
|
||||
clearStoredSession({ notifyAuthExpired: true })
|
||||
}
|
||||
}
|
||||
|
||||
initialized.value = true;
|
||||
initialized.value = true
|
||||
}
|
||||
|
||||
let loginPromise: Promise<UserInfo> | null = null;
|
||||
let loginPromise: Promise<UserInfo> | null = null
|
||||
|
||||
async function login(payload: LoginRequest): Promise<UserInfo> {
|
||||
if (loginPromise) {
|
||||
return loginPromise;
|
||||
return loginPromise
|
||||
}
|
||||
|
||||
const loginPayload = { ...payload };
|
||||
loginPromise = authApi.login(loginPayload).then((response) => {
|
||||
setStoredTokens({
|
||||
accessToken: response.access_token,
|
||||
refreshToken: response.refresh_token,
|
||||
expiresAt: response.expires_at,
|
||||
});
|
||||
setStoredUser(response.user);
|
||||
return response.user;
|
||||
}).finally(() => {
|
||||
loginPromise = null;
|
||||
});
|
||||
const loginPayload = { ...payload }
|
||||
loginPromise = authApi
|
||||
.login(loginPayload)
|
||||
.then((response) => {
|
||||
setStoredTokens({
|
||||
accessToken: response.access_token,
|
||||
refreshToken: response.refresh_token,
|
||||
expiresAt: response.expires_at,
|
||||
})
|
||||
setStoredUser(response.user)
|
||||
return response.user
|
||||
})
|
||||
.finally(() => {
|
||||
loginPromise = null
|
||||
})
|
||||
|
||||
return loginPromise;
|
||||
return loginPromise
|
||||
}
|
||||
|
||||
async function logout(): Promise<void> {
|
||||
try {
|
||||
if (accessToken.value) {
|
||||
await authApi.logout();
|
||||
await authApi.logout()
|
||||
}
|
||||
} finally {
|
||||
clearStoredSession();
|
||||
clearStoredSession()
|
||||
}
|
||||
}
|
||||
|
||||
const isAuthenticated = computed(() => Boolean(accessToken.value && user.value));
|
||||
const membership = computed(() => user.value?.membership ?? null);
|
||||
const isAuthenticated = computed(() => Boolean(accessToken.value && user.value))
|
||||
const membership = computed(() => user.value?.membership ?? null)
|
||||
const isMembershipBlocked = computed(() => {
|
||||
const value = membership.value;
|
||||
const value = membership.value
|
||||
return Boolean(
|
||||
value?.blocked ||
|
||||
value?.blocked_reason === "user_disabled" ||
|
||||
value?.status === "expired" ||
|
||||
value?.blocked_reason === "trial_plan_expired" ||
|
||||
value?.blocked_reason === "subscription_required" ||
|
||||
value?.blocked_reason === "subscription_inactive",
|
||||
);
|
||||
});
|
||||
const kolProfile = computed(() => user.value?.kol_profile ?? null);
|
||||
const isActiveKol = computed(() => kolProfile.value?.status === "active");
|
||||
value?.blocked_reason === 'user_disabled' ||
|
||||
value?.status === 'expired' ||
|
||||
value?.blocked_reason === 'trial_plan_expired' ||
|
||||
value?.blocked_reason === 'subscription_required' ||
|
||||
value?.blocked_reason === 'subscription_inactive',
|
||||
)
|
||||
})
|
||||
const kolProfile = computed(() => user.value?.kol_profile ?? null)
|
||||
const isActiveKol = computed(() => kolProfile.value?.status === 'active')
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
@@ -200,5 +206,5 @@ export const useAuthStore = defineStore("auth", () => {
|
||||
bootstrap,
|
||||
login,
|
||||
logout,
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { createPinia } from "pinia";
|
||||
|
||||
export const pinia = createPinia();
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
export const pinia = createPinia()
|
||||
|
||||
Reference in New Issue
Block a user