feat: add user role field and implement Gravatar fallback for user avatars
This commit is contained in:
@@ -40,6 +40,80 @@ export type AuthContextValue = {
|
||||
|
||||
const AuthContext = createContext<AuthContextValue | null>(null);
|
||||
|
||||
/**
|
||||
* Standard MD5 implementation for Gravatar support
|
||||
* (Self-contained to avoid dependency issues)
|
||||
*/
|
||||
function md5(string: string) {
|
||||
const k = Array.from({ length: 64 }, (_, i) => Math.floor(Math.abs(Math.sin(i + 1)) * 2 ** 32));
|
||||
let a = 0x67452301;
|
||||
let b = 0xefcdab89;
|
||||
let c = 0x98badcfe;
|
||||
let d = 0x10325476;
|
||||
const s = [
|
||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14,
|
||||
20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10,
|
||||
15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
|
||||
];
|
||||
|
||||
const str = unescape(encodeURIComponent(string));
|
||||
const msg = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; i++) msg[i] = str.charCodeAt(i);
|
||||
|
||||
const len = msg.length;
|
||||
const wordLen = ((len + 8) >> 6) + 1;
|
||||
const words = new Uint32Array(wordLen * 16);
|
||||
for (let i = 0; i < len; i++) words[i >> 2] |= msg[i] << ((i % 4) * 8);
|
||||
words[len >> 2] |= 0x80 << ((len % 4) * 8);
|
||||
words[wordLen * 16 - 2] = len * 8;
|
||||
|
||||
for (let i = 0; i < words.length; i += 16) {
|
||||
let [aa, bb, cc, dd] = [a, b, c, d];
|
||||
for (let j = 0; j < 64; j++) {
|
||||
let f: number;
|
||||
let g: number;
|
||||
if (j < 16) {
|
||||
f = (b & c) | (~b & d);
|
||||
g = j;
|
||||
} else if (j < 32) {
|
||||
f = (d & b) | (~d & c);
|
||||
g = (5 * j + 1) % 16;
|
||||
} else if (j < 48) {
|
||||
f = b ^ c ^ d;
|
||||
g = (3 * j + 5) % 16;
|
||||
} else {
|
||||
f = c ^ (b | ~d);
|
||||
g = (7 * j) % 16;
|
||||
}
|
||||
const temp = d;
|
||||
d = c;
|
||||
c = b;
|
||||
b =
|
||||
(b +
|
||||
((a + f + k[j] + words[i + g]) << s[j] | (a + f + k[j] + words[i + g]) >>> (32 - s[j]))) |
|
||||
0;
|
||||
a = temp;
|
||||
}
|
||||
a = (a + aa) | 0;
|
||||
b = (b + bb) | 0;
|
||||
c = (c + cc) | 0;
|
||||
d = (d + dd) | 0;
|
||||
}
|
||||
|
||||
return [a, b, c, d]
|
||||
.map((v) =>
|
||||
Array.from({ length: 4 }, (_, i) => ((v >> (i * 8)) & 0xff).toString(16).padStart(2, '0')).join(
|
||||
''
|
||||
)
|
||||
)
|
||||
.join('');
|
||||
}
|
||||
|
||||
function getGravatarUrl(email: string): string {
|
||||
const hash = md5(email.trim().toLowerCase());
|
||||
return `https://www.gravatar.com/avatar/${hash}?d=identicon`;
|
||||
}
|
||||
|
||||
function getDisplayName(user: User | null): string {
|
||||
if (!user) return 'Usuario';
|
||||
if (user.name?.trim()) return user.name.trim();
|
||||
@@ -53,7 +127,8 @@ function getInitials(name: string): string {
|
||||
}
|
||||
|
||||
function getAvatarUrl(user: User | null): string | null {
|
||||
return user?.image || null;
|
||||
if (!user) return null;
|
||||
return user.image || getGravatarUrl(user.email);
|
||||
}
|
||||
|
||||
export function AuthProvider({ children }: PropsWithChildren) {
|
||||
|
||||
Reference in New Issue
Block a user