refactor(auth): migrate fully to HTTP-only cookie sessions
Remove client-side token storage and Authorization header injection Backend login now only returns user data, omitting the session token Remove Authorization from backend CORS allowed headers Clean up obsolete VITE_* environment variable fallbacks Update Modal component to use Vue useId() instead of Math.random()
This commit is contained in:
@@ -21,7 +21,7 @@ import {
|
||||
type AppIcon
|
||||
} from './src/icons';
|
||||
import { getCurrentLocale, loadSystemWordings, onLocaleChange, setCurrentLocale } from './src/i18n';
|
||||
import { api, getAuthToken, onAuthTokenChange, setAuthToken, type AuthUser, type Language } from './src/services/api';
|
||||
import { api, notifyAuthChange, onAuthChange, type AuthUser, type Language } from './src/services/api';
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const router = useRouter();
|
||||
@@ -117,9 +117,6 @@ async function loadCurrentUser() {
|
||||
currentUser.value = response.user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
if (getAuthToken()) {
|
||||
setAuthToken(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +128,7 @@ async function logout() {
|
||||
}
|
||||
|
||||
currentUser.value = null;
|
||||
setAuthToken(null);
|
||||
notifyAuthChange();
|
||||
await router.push('/');
|
||||
}
|
||||
|
||||
@@ -160,7 +157,7 @@ async function updateLocale(value: string) {
|
||||
onMounted(() => {
|
||||
void loadLanguages();
|
||||
void loadCurrentUser();
|
||||
removeAuthListener = onAuthTokenChange(() => {
|
||||
removeAuthListener = onAuthChange(() => {
|
||||
void loadCurrentUser();
|
||||
});
|
||||
removeLocaleListener = onLocaleChange(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { api, setAuthToken } from '../src/services/api';
|
||||
import { api } from '../src/services/api';
|
||||
|
||||
export default defineNuxtRouteMiddleware(async (to) => {
|
||||
const requiredPermissions = to.matched
|
||||
@@ -30,7 +30,6 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
||||
return navigateTo('/pokemon');
|
||||
}
|
||||
} catch {
|
||||
setAuthToken(null);
|
||||
return navigateTo({ path: '/login', query: { redirect: to.fullPath } });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,13 +12,11 @@ export default defineNuxtConfig({
|
||||
runtimeConfig: {
|
||||
serverApiBaseUrl:
|
||||
process.env.NUXT_SERVER_API_BASE_URL ??
|
||||
process.env.NUXT_API_BASE_URL ??
|
||||
process.env.NUXT_PUBLIC_API_BASE_URL ??
|
||||
process.env.VITE_API_BASE_URL ??
|
||||
'http://localhost:3001',
|
||||
public: {
|
||||
apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL ?? process.env.VITE_API_BASE_URL ?? 'http://localhost:3001',
|
||||
siteUrl: normalizeSiteUrl(process.env.NUXT_PUBLIC_SITE_URL ?? process.env.VITE_SITE_URL)
|
||||
apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL ?? 'http://localhost:3001',
|
||||
siteUrl: normalizeSiteUrl(process.env.NUXT_PUBLIC_SITE_URL)
|
||||
}
|
||||
},
|
||||
app: {
|
||||
|
||||
@@ -8,10 +8,8 @@ import Tabs, { type TabOption } from './Tabs.vue';
|
||||
import { iconCancel, iconComment, iconDelete, iconReactionLike, iconReply, iconWarning } from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
moderationUpdateEvent,
|
||||
onAuthTokenChange,
|
||||
setAuthToken,
|
||||
onAuthChange,
|
||||
type AiModerationStatus,
|
||||
type AuthUser,
|
||||
type CommentSort,
|
||||
@@ -77,18 +75,11 @@ const sortOptions = computed<Array<{ value: CommentSort; label: string }>>(() =>
|
||||
async function loadCurrentUser() {
|
||||
authReady.value = false;
|
||||
|
||||
if (!getAuthToken()) {
|
||||
currentUser.value = null;
|
||||
authReady.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.me();
|
||||
currentUser.value = response.user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
setAuthToken(null);
|
||||
} finally {
|
||||
authReady.value = true;
|
||||
}
|
||||
@@ -515,7 +506,7 @@ onMounted(() => {
|
||||
void loadCurrentUser();
|
||||
void loadLanguages();
|
||||
void loadDiscussion();
|
||||
removeAuthListener = onAuthTokenChange(() => {
|
||||
removeAuthListener = onAuthChange(() => {
|
||||
void loadCurrentUser();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ let openModalCount = 0;
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { nextTick, onBeforeUnmount, onMounted, onUpdated, ref, watch } from 'vue';
|
||||
import { nextTick, onBeforeUnmount, onMounted, onUpdated, ref, useId, watch } from 'vue';
|
||||
import { iconClose } from '../icons';
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -29,7 +29,7 @@ const emit = defineEmits<{
|
||||
close: [];
|
||||
}>();
|
||||
|
||||
const titleId = `modal-title-${Math.random().toString(36).slice(2)}`;
|
||||
const titleId = useId();
|
||||
const dialog = ref<HTMLElement | null>(null);
|
||||
const modalBody = ref<HTMLElement | null>(null);
|
||||
const closeButton = ref<HTMLButtonElement | null>(null);
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
} from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
moderationUpdateEvent,
|
||||
notificationWebSocketUrl,
|
||||
type AuthUser,
|
||||
@@ -92,7 +91,7 @@ function disconnectNotifications() {
|
||||
|
||||
function scheduleReconnect() {
|
||||
clearReconnectTimer();
|
||||
if (stopped || !props.currentUser || !getAuthToken()) {
|
||||
if (stopped || !props.currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -118,7 +117,7 @@ function isNotificationWsMessage(value: unknown): value is NotificationWsMessage
|
||||
}
|
||||
|
||||
async function connectNotifications() {
|
||||
if (!props.currentUser || !getAuthToken() || typeof WebSocket === 'undefined') {
|
||||
if (!props.currentUser || typeof WebSocket === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { getCurrentLocale } from '../i18n';
|
||||
|
||||
let browserApiBaseUrl = 'http://localhost:3001';
|
||||
let serverApiBaseUrl = 'http://localhost:3001';
|
||||
const authTokenKey = 'pokopia_auth_token';
|
||||
const authChangeEvent = 'pokopia-auth-change';
|
||||
|
||||
export interface ApiRequestOptions {
|
||||
@@ -807,7 +806,6 @@ export interface RegisterPayload extends LoginPayload {
|
||||
}
|
||||
|
||||
export interface AuthResponse {
|
||||
token: string;
|
||||
user: AuthUser;
|
||||
}
|
||||
|
||||
@@ -1061,40 +1059,7 @@ export function buildQuery(params: Record<string, string | number | boolean | nu
|
||||
return query ? `?${query}` : '';
|
||||
}
|
||||
|
||||
function authStorage(type: 'local' | 'session'): Storage | null {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return type === 'local' ? window.localStorage : window.sessionStorage;
|
||||
}
|
||||
|
||||
export function getAuthToken(): string | null {
|
||||
const sessionToken = authStorage('session')?.getItem(authTokenKey);
|
||||
return sessionToken ?? authStorage('local')?.getItem(authTokenKey) ?? null;
|
||||
}
|
||||
|
||||
export function setAuthToken(token: string | null, options: { persistent?: boolean } = {}): void {
|
||||
const local = authStorage('local');
|
||||
const session = authStorage('session');
|
||||
|
||||
if (token) {
|
||||
if (options.persistent === false) {
|
||||
session?.setItem(authTokenKey, token);
|
||||
local?.removeItem(authTokenKey);
|
||||
} else {
|
||||
local?.setItem(authTokenKey, token);
|
||||
session?.removeItem(authTokenKey);
|
||||
}
|
||||
} else {
|
||||
local?.removeItem(authTokenKey);
|
||||
session?.removeItem(authTokenKey);
|
||||
}
|
||||
|
||||
notifyAuthChange();
|
||||
}
|
||||
|
||||
export function onAuthTokenChange(callback: () => void): () => void {
|
||||
export function onAuthChange(callback: () => void): () => void {
|
||||
if (typeof window === 'undefined') {
|
||||
return () => {};
|
||||
}
|
||||
@@ -1111,12 +1076,7 @@ export function notifyAuthChange(): void {
|
||||
|
||||
function requestHeaders(extraHeaders?: HeadersInit): Headers {
|
||||
const headers = new Headers(extraHeaders);
|
||||
const token = getAuthToken();
|
||||
headers.set('X-Locale', headers.get('X-Locale') ?? getCurrentLocale());
|
||||
if (token && !headers.has('Authorization')) {
|
||||
headers.set('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import Tabs, { type TabOption } from '../components/Tabs.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd, iconArtifact } from '../icons';
|
||||
import { api, getAuthToken, type AncientArtifact, type AuthUser, type ListPage, type Options } from '../services/api';
|
||||
import { api, type AncientArtifact, type AuthUser, type ListPage, type Options } from '../services/api';
|
||||
import ItemEdit from './ItemEdit.vue';
|
||||
|
||||
const route = useRoute();
|
||||
@@ -150,12 +150,10 @@ function loadMoreArtifacts() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!options.value) {
|
||||
try {
|
||||
|
||||
@@ -13,7 +13,6 @@ import TranslationFields from '../components/TranslationFields.vue';
|
||||
import { iconAdd, iconCancel, iconDelete, iconDish, iconEdit, iconItem, iconSave } from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
type AuthUser,
|
||||
type Dish,
|
||||
type DishCategory,
|
||||
@@ -301,12 +300,10 @@ async function loadEditorOptions() {
|
||||
|
||||
async function loadPage() {
|
||||
loading.value = true;
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
await Promise.all([initialCategoriesLoaded.value ? Promise.resolve() : loadDish(), loadEditorOptions()]);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import LoadMoreSentinel from '../components/LoadMoreSentinel.vue';
|
||||
import PageHeader from '../components/PageHeader.vue';
|
||||
import Skeleton from '../components/Skeleton.vue';
|
||||
import { iconAdd, iconHabitat } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type Habitat, type ListPage } from '../services/api';
|
||||
import { api, type AuthUser, type Habitat, type ListPage } from '../services/api';
|
||||
import HabitatEdit from './HabitatEdit.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -125,12 +125,10 @@ function loadMoreHabitats() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!initialPageLoaded.value) {
|
||||
await loadHabitats();
|
||||
|
||||
@@ -11,7 +11,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import Tabs, { type TabOption } from '../components/Tabs.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd, iconChevronDown, iconChevronUp, iconItem } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type Item, type ListPage, type Options } from '../services/api';
|
||||
import { api, type AuthUser, type Item, type ListPage, type Options } from '../services/api';
|
||||
import ItemEdit from './ItemEdit.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -527,12 +527,10 @@ function loadMoreItems() {
|
||||
onMounted(async () => {
|
||||
document.addEventListener('pointerdown', onCreateDefaultsDocumentPointerDown);
|
||||
document.addEventListener('keydown', onDocumentKeydown);
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!options.value) {
|
||||
try {
|
||||
|
||||
@@ -28,10 +28,8 @@ import {
|
||||
} from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
moderationUpdateEvent,
|
||||
onAuthTokenChange,
|
||||
setAuthToken,
|
||||
onAuthChange,
|
||||
type AiModerationStatus,
|
||||
type AuthUser,
|
||||
type CommentSort,
|
||||
@@ -112,17 +110,11 @@ function summaryText(value: string, maxLength: number) {
|
||||
}
|
||||
|
||||
async function loadCurrentUser() {
|
||||
if (!getAuthToken()) {
|
||||
currentUser.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.me();
|
||||
currentUser.value = response.user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
setAuthToken(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,14 +832,13 @@ onMounted(() => {
|
||||
document.addEventListener('click', closeReactionPickerFromDocument);
|
||||
document.addEventListener('keydown', closeReactionPickerFromKeyboard);
|
||||
window.addEventListener(moderationUpdateEvent, handleModerationUpdate);
|
||||
const hadAuthToken = getAuthToken() !== null;
|
||||
void (async () => {
|
||||
await loadCurrentUser();
|
||||
if (!initialPostLoaded.value || hadAuthToken) {
|
||||
if (!initialPostLoaded.value || currentUser.value) {
|
||||
await loadPost();
|
||||
}
|
||||
})();
|
||||
removeAuthListener = onAuthTokenChange(() => {
|
||||
removeAuthListener = onAuthChange(() => {
|
||||
void loadCurrentUser();
|
||||
void loadPost();
|
||||
});
|
||||
|
||||
@@ -35,10 +35,8 @@ import {
|
||||
} from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
moderationUpdateEvent,
|
||||
onAuthTokenChange,
|
||||
setAuthToken,
|
||||
onAuthChange,
|
||||
type AiModerationStatus,
|
||||
type AuthUser,
|
||||
type CommentSort,
|
||||
@@ -252,20 +250,12 @@ const submitLabel = computed(() => {
|
||||
async function loadCurrentUser() {
|
||||
authReady.value = false;
|
||||
|
||||
if (!getAuthToken()) {
|
||||
currentUser.value = null;
|
||||
activeFeedScope.value = 'all';
|
||||
authReady.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.me();
|
||||
currentUser.value = response.user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
activeFeedScope.value = 'all';
|
||||
setAuthToken(null);
|
||||
} finally {
|
||||
authReady.value = true;
|
||||
}
|
||||
@@ -1376,7 +1366,6 @@ onMounted(() => {
|
||||
document.addEventListener('click', closeReactionPickerFromDocument);
|
||||
document.addEventListener('keydown', closeReactionPickerFromKeyboard);
|
||||
window.addEventListener(moderationUpdateEvent, handleModerationUpdate);
|
||||
const hadAuthToken = getAuthToken() !== null;
|
||||
void (async () => {
|
||||
await loadCurrentUser();
|
||||
if (!initialLanguagesLoaded.value) {
|
||||
@@ -1387,12 +1376,12 @@ onMounted(() => {
|
||||
await loadLifeCategories();
|
||||
initialOptionsLoaded.value = true;
|
||||
}
|
||||
if (!initialPostsLoaded.value || hadAuthToken) {
|
||||
if (!initialPostsLoaded.value || currentUser.value) {
|
||||
await loadPosts();
|
||||
initialPostsLoaded.value = true;
|
||||
}
|
||||
})();
|
||||
removeAuthListener = onAuthTokenChange(() => {
|
||||
removeAuthListener = onAuthChange(() => {
|
||||
void (async () => {
|
||||
await loadCurrentUser();
|
||||
await loadPosts();
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useRoute, useRouter } from 'vue-router';
|
||||
import PageHeader from '../components/PageHeader.vue';
|
||||
import StatusMessage from '../components/StatusMessage.vue';
|
||||
import { iconLogin } from '../icons';
|
||||
import { api, setAuthToken } from '../services/api';
|
||||
import { api, notifyAuthChange } from '../services/api';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -22,12 +22,12 @@ async function submitLogin() {
|
||||
errorMessage.value = '';
|
||||
|
||||
try {
|
||||
const response = await api.login({
|
||||
await api.login({
|
||||
email: email.value,
|
||||
password: password.value,
|
||||
rememberMe: rememberMe.value
|
||||
});
|
||||
setAuthToken(response.token, { persistent: rememberMe.value });
|
||||
notifyAuthChange();
|
||||
|
||||
const redirect =
|
||||
typeof route.query.redirect === 'string' && route.query.redirect.startsWith('/')
|
||||
|
||||
@@ -10,7 +10,7 @@ import PageHeader from '../components/PageHeader.vue';
|
||||
import Skeleton from '../components/Skeleton.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type ListPage, type Options, type Pokemon } from '../services/api';
|
||||
import { api, type AuthUser, type ListPage, type Options, type Pokemon } from '../services/api';
|
||||
import PokemonEdit from './PokemonEdit.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -158,12 +158,10 @@ function pokemonCardImage(item: Pokemon) {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!options.value) {
|
||||
try {
|
||||
|
||||
@@ -12,7 +12,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import Tabs, { type TabOption } from '../components/Tabs.vue';
|
||||
import { iconBack, iconEdit, iconRecipe } from '../icons';
|
||||
import { applySeo, resolvedSeoHead, resolveSeo } from '../seo';
|
||||
import { api, getAuthToken, type AuthUser, type RecipeDetail } from '../services/api';
|
||||
import { api, type AuthUser, type RecipeDetail } from '../services/api';
|
||||
import RecipeEdit from './RecipeEdit.vue';
|
||||
|
||||
const route = useRoute();
|
||||
@@ -96,12 +96,10 @@ async function loadRecipeDetail() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!initialRecipeLoaded.value) {
|
||||
await loadRecipeDetail();
|
||||
|
||||
@@ -8,7 +8,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import StatusMessage from '../components/StatusMessage.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd, iconCancel, iconDelete, iconSave } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type ConfigType, type Item, type Options, type RecipePayload } from '../services/api';
|
||||
import { api, type AuthUser, type ConfigType, type Item, type Options, type RecipePayload } from '../services/api';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -105,11 +105,6 @@ async function loadOptions() {
|
||||
}
|
||||
|
||||
async function loadCurrentUser() {
|
||||
if (!getAuthToken()) {
|
||||
currentUser.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
|
||||
@@ -11,7 +11,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import Tabs, { type TabOption } from '../components/Tabs.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd, iconNoRecipe, iconRecipe } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type Item, type ListPage, type Options } from '../services/api';
|
||||
import { api, type AuthUser, type Item, type ListPage, type Options } from '../services/api';
|
||||
import RecipeEdit from './RecipeEdit.vue';
|
||||
|
||||
const options = ref<Options | null>(null);
|
||||
@@ -170,12 +170,10 @@ function loadMoreItems() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!options.value) {
|
||||
try {
|
||||
|
||||
@@ -26,9 +26,7 @@ import {
|
||||
} from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
notifyAuthChange,
|
||||
setAuthToken,
|
||||
type AuthUser,
|
||||
type DiscussionEntityType,
|
||||
type LifePost,
|
||||
@@ -229,7 +227,6 @@ feeds.value = initialPublicProfile.value.feeds?.items ?? [];
|
||||
feedsCursor.value = initialPublicProfile.value.feeds?.nextCursor ?? null;
|
||||
feedsHasMore.value = initialPublicProfile.value.feeds?.hasMore ?? false;
|
||||
const initialPublicProfileLoaded = ref(initialPublicProfile.value.profile !== null);
|
||||
const initialFeedsLoaded = ref(initialPublicProfile.value.feeds !== null);
|
||||
loading.value = !initialPublicProfileLoaded.value;
|
||||
const profileSeo = computed(() =>
|
||||
profile.value && !isAccountRoute.value
|
||||
@@ -324,18 +321,12 @@ function resetActivity() {
|
||||
}
|
||||
|
||||
async function loadOptionalCurrentUser() {
|
||||
if (!getAuthToken()) {
|
||||
currentUser.value = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.me();
|
||||
currentUser.value = response.user;
|
||||
return response.user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
setAuthToken(null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -723,11 +714,7 @@ function commentTargetTitle(comment: UserCommentActivity): string {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (isAccountRoute.value || getAuthToken() || !initialPublicProfileLoaded.value) {
|
||||
void loadProfile();
|
||||
} else if (!initialFeedsLoaded.value) {
|
||||
void loadFeeds(true);
|
||||
}
|
||||
void loadProfile();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user