Infrastructure: MinIO service added to docker-compose (prod + dev) with minio-init container that creates the avatars bucket with public read policy. Traefik routes minio.task.beetech.dev → port 9000. Backend: native MinIO SDK integration via MinioStorageService; UploadUserPhotoUseCase validates 5 MB limit and image content-type; new POST /auth/profile/photo endpoint (multipart). All hardcoded ui-avatars.com fallback URLs removed from 6 use cases — backend now returns null when user has no photo. Frontend: ProfilePage gains clickable avatar with camera overlay, file picker, preview, and photo upload on save. UserAvatar gains onError handler for broken URLs and xs size. OrganizationAdminPage replaces hardcoded ui-avatars.com with UserAvatar component. CI/CD: deploy-main.yml passes 5 new MinIO secrets to the server and writes them to .env.production. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
34 lines
743 B
TypeScript
34 lines
743 B
TypeScript
import { apiFetch } from '../lib/api'
|
|
|
|
export interface UpdateProfilePayload {
|
|
name: string
|
|
email: string
|
|
}
|
|
|
|
export interface UpdateProfileResult {
|
|
name: string
|
|
email: string
|
|
photo: string
|
|
}
|
|
|
|
export interface UploadPhotoResult {
|
|
photoUrl: string
|
|
}
|
|
|
|
export const userService = {
|
|
updateProfile: async (payload: UpdateProfilePayload): Promise<UpdateProfileResult> => {
|
|
return apiFetch<UpdateProfileResult>('/auth/profile', {
|
|
method: 'PUT',
|
|
body: payload,
|
|
})
|
|
},
|
|
|
|
uploadPhoto: async (file: File): Promise<UploadPhotoResult> => {
|
|
const form = new FormData()
|
|
form.append('photo', file)
|
|
return apiFetch<UploadPhotoResult>('/auth/profile/photo', {
|
|
method: 'POST',
|
|
body: form,
|
|
})
|
|
},
|
|
}
|