mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-17 01:11:38 +03:00
fix: ignore profile picture cache after profile picture gets updated
This commit is contained in:
@@ -254,7 +254,10 @@ func (uc *UserController) getUserProfilePictureHandler(c *gin.Context) {
|
|||||||
defer picture.Close()
|
defer picture.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Header("Cache-Control", "public, max-age=300")
|
_, ok := c.GetQuery("skipCache")
|
||||||
|
if !ok {
|
||||||
|
c.Header("Cache-Control", "public, max-age=900")
|
||||||
|
}
|
||||||
|
|
||||||
c.DataFromReader(http.StatusOK, size, "image/png", picture, nil)
|
c.DataFromReader(http.StatusOK, size, "image/png", picture, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
import FileInput from '$lib/components/form/file-input.svelte';
|
import FileInput from '$lib/components/form/file-input.svelte';
|
||||||
import * as Avatar from '$lib/components/ui/avatar';
|
import * as Avatar from '$lib/components/ui/avatar';
|
||||||
import Button from '$lib/components/ui/button/button.svelte';
|
import Button from '$lib/components/ui/button/button.svelte';
|
||||||
import { LucideLoader, LucideRefreshCw, LucideUpload } from 'lucide-svelte';
|
|
||||||
import { openConfirmDialog } from '../confirm-dialog';
|
|
||||||
import { m } from '$lib/paraglide/messages';
|
import { m } from '$lib/paraglide/messages';
|
||||||
import type UserService from '$lib/services/user-service';
|
import { getProfilePictureUrl } from '$lib/utils/profile-picture-util';
|
||||||
|
import { LucideLoader, LucideRefreshCw, LucideUpload } from 'lucide-svelte';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { openConfirmDialog } from '../confirm-dialog';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
userId,
|
userId,
|
||||||
@@ -20,7 +21,12 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
let isLoading = $state(false);
|
let isLoading = $state(false);
|
||||||
let imageDataURL = $state(`/api/users/${userId}/profile-picture.png`);
|
let imageDataURL = $state('');
|
||||||
|
onMount(() => {
|
||||||
|
// The "skipCache" query will only be added to the profile picture url on client-side
|
||||||
|
// because of that we need to set the imageDataURL after the component is mounted
|
||||||
|
imageDataURL = getProfilePictureUrl(userId);
|
||||||
|
});
|
||||||
|
|
||||||
async function onImageChange(e: Event) {
|
async function onImageChange(e: Event) {
|
||||||
const file = (e.target as HTMLInputElement).files?.[0] || null;
|
const file = (e.target as HTMLInputElement).files?.[0] || null;
|
||||||
@@ -35,7 +41,7 @@
|
|||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
|
|
||||||
await updateCallback(file).catch(() => {
|
await updateCallback(file).catch(() => {
|
||||||
imageDataURL = `/api/users/${userId}/profile-picture.png`;
|
imageDataURL = getProfilePictureUrl(userId);
|
||||||
});
|
});
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { m } from '$lib/paraglide/messages';
|
import { m } from '$lib/paraglide/messages';
|
||||||
import WebAuthnService from '$lib/services/webauthn-service';
|
import WebAuthnService from '$lib/services/webauthn-service';
|
||||||
import userStore from '$lib/stores/user-store';
|
import userStore from '$lib/stores/user-store';
|
||||||
|
import { getProfilePictureUrl } from '$lib/utils/profile-picture-util';
|
||||||
import { LucideLogOut, LucideUser } from 'lucide-svelte';
|
import { LucideLogOut, LucideUser } from 'lucide-svelte';
|
||||||
|
|
||||||
const webauthnService = new WebAuthnService();
|
const webauthnService = new WebAuthnService();
|
||||||
@@ -17,7 +18,7 @@
|
|||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger
|
<DropdownMenu.Trigger
|
||||||
><Avatar.Root class="h-9 w-9">
|
><Avatar.Root class="h-9 w-9">
|
||||||
<Avatar.Image src="/api/users/{$userStore?.id}/profile-picture.png" />
|
<Avatar.Image src={getProfilePictureUrl($userStore?.id)} />
|
||||||
</Avatar.Root></DropdownMenu.Trigger
|
</Avatar.Root></DropdownMenu.Trigger
|
||||||
>
|
>
|
||||||
<DropdownMenu.Content class="min-w-40" align="start">
|
<DropdownMenu.Content class="min-w-40" align="start">
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import userStore from '$lib/stores/user-store';
|
||||||
import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type';
|
import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type';
|
||||||
import type { UserGroup } from '$lib/types/user-group.type';
|
import type { UserGroup } from '$lib/types/user-group.type';
|
||||||
import type { User, UserCreate } from '$lib/types/user.type';
|
import type { User, UserCreate } from '$lib/types/user.type';
|
||||||
|
import { bustProfilePictureCache } from '$lib/utils/profile-picture-util';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
import APIService from './api-service';
|
import APIService from './api-service';
|
||||||
|
|
||||||
export default class UserService extends APIService {
|
export default class UserService extends APIService {
|
||||||
@@ -49,6 +52,7 @@ export default class UserService extends APIService {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', image!);
|
formData.append('file', image!);
|
||||||
|
|
||||||
|
bustProfilePictureCache(userId);
|
||||||
await this.api.put(`/users/${userId}/profile-picture`, formData);
|
await this.api.put(`/users/${userId}/profile-picture`, formData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,14 +60,17 @@ export default class UserService extends APIService {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', image!);
|
formData.append('file', image!);
|
||||||
|
|
||||||
|
bustProfilePictureCache(get(userStore)!.id);
|
||||||
await this.api.put('/users/me/profile-picture', formData);
|
await this.api.put('/users/me/profile-picture', formData);
|
||||||
}
|
}
|
||||||
|
|
||||||
async resetCurrentUserProfilePicture() {
|
async resetCurrentUserProfilePicture() {
|
||||||
|
bustProfilePictureCache(get(userStore)!.id);
|
||||||
await this.api.delete(`/users/me/profile-picture`);
|
await this.api.delete(`/users/me/profile-picture`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async resetProfilePicture(userId: string) {
|
async resetProfilePicture(userId: string) {
|
||||||
|
bustProfilePictureCache(userId);
|
||||||
await this.api.delete(`/users/${userId}/profile-picture`);
|
await this.api.delete(`/users/${userId}/profile-picture`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
38
frontend/src/lib/utils/profile-picture-util.ts
Normal file
38
frontend/src/lib/utils/profile-picture-util.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { browser } from '$app/environment';
|
||||||
|
|
||||||
|
type SkipCacheUntil = {
|
||||||
|
[key: string]: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getProfilePictureUrl(userId?: string) {
|
||||||
|
if (!userId) return '';
|
||||||
|
|
||||||
|
let url = `/api/users/${userId}/profile-picture.png`;
|
||||||
|
|
||||||
|
if (browser) {
|
||||||
|
const skipCacheUntil = getSkipCacheUntil(userId);
|
||||||
|
const skipCache = skipCacheUntil > Date.now();
|
||||||
|
if (skipCache) {
|
||||||
|
const skipCacheParam = new URLSearchParams();
|
||||||
|
skipCacheParam.append('skip-cache', skipCacheUntil.toString());
|
||||||
|
url += '?' + skipCacheParam.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSkipCacheUntil(userId: string) {
|
||||||
|
const skipCacheUntil: SkipCacheUntil = JSON.parse(
|
||||||
|
localStorage.getItem('skip-cache-until') ?? '{}'
|
||||||
|
);
|
||||||
|
return skipCacheUntil[userId] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bustProfilePictureCache(userId: string) {
|
||||||
|
const skipCacheUntil: SkipCacheUntil = JSON.parse(
|
||||||
|
localStorage.getItem('skip-cache-until') ?? '{}'
|
||||||
|
);
|
||||||
|
skipCacheUntil[userId] = Date.now() + 1000 * 60 * 15; // 15 minutes
|
||||||
|
localStorage.setItem('skip-cache-until', JSON.stringify(skipCacheUntil));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user