feature(up-dow-avatar): tidy avatar upload, enforce BearerAuth, add apidog types
This commit is contained in:
parent
3c52ec5e3a
commit
47ef207454
@ -4,6 +4,7 @@
|
|||||||
"@heroicons/react": "^2.2.0",
|
"@heroicons/react": "^2.2.0",
|
||||||
"@supabase/supabase-js": "^2.75.0",
|
"@supabase/supabase-js": "^2.75.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"next": "^16.0.0",
|
||||||
"react-big-calendar": "^1.19.4",
|
"react-big-calendar": "^1.19.4",
|
||||||
"react-signature-canvas": "^1.1.0-alpha.2"
|
"react-signature-canvas": "^1.1.0-alpha.2"
|
||||||
}
|
}
|
||||||
|
|||||||
1107
pnpm-lock.yaml
generated
Normal file
1107
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -2771,30 +2771,27 @@ export async function uploadFotoPaciente(_id: string | number, _file: File): Pro
|
|||||||
};
|
};
|
||||||
const ext = extMap[_file.type] || 'jpg';
|
const ext = extMap[_file.type] || 'jpg';
|
||||||
|
|
||||||
// O bucket deve ser 'avatars' e o caminho do objeto será userId/avatar.ext
|
|
||||||
const bucket = 'avatars';
|
|
||||||
const objectPath = `${userId}/avatar.${ext}`;
|
const objectPath = `${userId}/avatar.${ext}`;
|
||||||
const uploadUrl = `${ENV_CONFIG.SUPABASE_URL}/storage/v1/object/${bucket}/${encodeURIComponent(objectPath)}`;
|
const uploadUrl = `https://mock.apidog.com/m1/1053378-0-default/storage/v1/object/avatars/${encodeURI(objectPath)}`;
|
||||||
|
|
||||||
// Build multipart form data
|
|
||||||
const form = new FormData();
|
const form = new FormData();
|
||||||
form.append('file', _file, `avatar.${ext}`);
|
form.append('file', _file, `avatar.${ext}`);
|
||||||
|
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
// Supabase requires the anon key in 'apikey' header for client-side uploads
|
Accept: 'application/json'
|
||||||
apikey: ENV_CONFIG.SUPABASE_ANON_KEY,
|
|
||||||
// Accept json
|
|
||||||
Accept: 'application/json',
|
|
||||||
};
|
};
|
||||||
// if user is logged in, include Authorization header
|
|
||||||
const jwt = getAuthToken();
|
const jwt = getAuthToken();
|
||||||
if (jwt) headers.Authorization = `Bearer ${jwt}`;
|
if (!jwt) {
|
||||||
|
throw new Error('Autenticação necessária: token JWT obrigatório para upload de avatar');
|
||||||
|
}
|
||||||
|
headers.Authorization = `Bearer ${jwt}`;
|
||||||
|
|
||||||
console.debug('[uploadFotoPaciente] Iniciando upload:', {
|
console.debug('[uploadFotoPaciente] Iniciando upload:', {
|
||||||
url: uploadUrl,
|
url: uploadUrl,
|
||||||
fileType: _file.type,
|
fileType: _file.type,
|
||||||
fileSize: _file.size,
|
fileSize: _file.size,
|
||||||
hasAuth: !!jwt
|
hasAuth: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await fetch(uploadUrl, {
|
const res = await fetch(uploadUrl, {
|
||||||
@ -2827,7 +2824,7 @@ export async function uploadFotoPaciente(_id: string | number, _file: File): Pro
|
|||||||
|
|
||||||
// The API may not return a structured body; return the Key we constructed
|
// The API may not return a structured body; return the Key we constructed
|
||||||
const key = (json && (json.Key || json.key)) ?? objectPath;
|
const key = (json && (json.Key || json.key)) ?? objectPath;
|
||||||
const publicUrl = `${ENV_CONFIG.SUPABASE_URL}/storage/v1/object/public/avatars/${encodeURIComponent(userId)}/avatar.${ext}`;
|
const publicUrl = `https://mock.apidog.com/m1/1053378-0-default/storage/v1/object/avatars/${encodeURIComponent(userId)}/avatar.${ext}`;
|
||||||
return { foto_url: publicUrl, Key: key };
|
return { foto_url: publicUrl, Key: key };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2842,21 +2839,22 @@ export function getAvatarPublicUrl(userId: string | number): string {
|
|||||||
// Example: https://<project>.supabase.co/storage/v1/object/public/avatars/{userId}/avatar
|
// Example: https://<project>.supabase.co/storage/v1/object/public/avatars/{userId}/avatar
|
||||||
const id = String(userId || '').trim();
|
const id = String(userId || '').trim();
|
||||||
if (!id) throw new Error('userId é obrigatório para obter URL pública do avatar');
|
if (!id) throw new Error('userId é obrigatório para obter URL pública do avatar');
|
||||||
const base = String(ENV_CONFIG.SUPABASE_URL).replace(/\/$/, '');
|
return `https://mock.apidog.com/m1/1053378-0-default/storage/v1/object/avatars/${encodeURIComponent(id)}/avatar`;
|
||||||
// Note: Supabase public object path does not require an extension in some setups
|
|
||||||
return `${base}/storage/v1/object/public/${encodeURIComponent('avatars')}/${encodeURIComponent(id)}/avatar`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removerFotoPaciente(_id: string | number): Promise<void> {
|
export async function removerFotoPaciente(_id: string | number): Promise<void> {
|
||||||
const userId = String(_id || '').trim();
|
const userId = String(_id || '').trim();
|
||||||
if (!userId) throw new Error('ID do paciente é obrigatório para remover foto');
|
if (!userId) throw new Error('ID do paciente é obrigatório para remover foto');
|
||||||
const deleteUrl = `${ENV_CONFIG.SUPABASE_URL}/storage/v1/object/avatars/${encodeURIComponent(userId)}/avatar`;
|
const objectPath = `${userId}/avatar`;
|
||||||
|
const deleteUrl = `https://mock.apidog.com/m1/1053378-0-default/storage/v1/object/avatars/${encodeURI(objectPath)}`;
|
||||||
const headers: Record<string,string> = {
|
const headers: Record<string,string> = {
|
||||||
apikey: ENV_CONFIG.SUPABASE_ANON_KEY,
|
Accept: 'application/json'
|
||||||
Accept: 'application/json',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Require auth for delete (follow security expectations)
|
||||||
const jwt = getAuthToken();
|
const jwt = getAuthToken();
|
||||||
if (jwt) headers.Authorization = `Bearer ${jwt}`;
|
if (!jwt) throw new Error('Autenticação necessária: token JWT obrigatório para remover avatar');
|
||||||
|
headers.Authorization = `Bearer ${jwt}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.debug('[removerFotoPaciente] Deleting avatar for user:', userId, 'url:', deleteUrl);
|
console.debug('[removerFotoPaciente] Deleting avatar for user:', userId, 'url:', deleteUrl);
|
||||||
|
|||||||
11
susconecta/types/apidog.ts
Normal file
11
susconecta/types/apidog.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Types for APIdog/OpenAPI helper models
|
||||||
|
export interface ApidogModel {
|
||||||
|
/**
|
||||||
|
* Path parameter used by the mock/OpenAPI for storage endpoints.
|
||||||
|
* Example: "user-123/avatar.jpg"
|
||||||
|
*/
|
||||||
|
path: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApidogModel;
|
||||||
Loading…
x
Reference in New Issue
Block a user