hazo_auth 1.6.1 → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +259 -1
- package/SETUP_CHECKLIST.md +89 -3
- package/dist/app/api/hazo_auth/me/route.d.ts +30 -1
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/me/route.js +76 -16
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +5 -0
- package/dist/client.d.ts +8 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +25 -0
- package/dist/components/layouts/email_verification/index.d.ts +2 -1
- package/dist/components/layouts/email_verification/index.d.ts.map +1 -1
- package/dist/components/layouts/email_verification/index.js +3 -2
- package/dist/components/layouts/forgot_password/index.d.ts +3 -1
- package/dist/components/layouts/forgot_password/index.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/index.js +3 -2
- package/dist/components/layouts/my_settings/components/editable_field.js +1 -1
- package/dist/components/layouts/my_settings/components/password_change_dialog.js +1 -1
- package/dist/components/layouts/my_settings/components/profile_picture_display.js +1 -1
- package/dist/components/layouts/my_settings/components/profile_picture_gravatar_tab.js +1 -1
- package/dist/components/layouts/my_settings/components/profile_picture_library_tab.js +4 -4
- package/dist/components/layouts/my_settings/components/profile_picture_upload_tab.js +4 -4
- package/dist/components/layouts/my_settings/index.js +1 -1
- package/dist/components/layouts/shared/components/profile_pic_menu.d.ts +6 -2
- package/dist/components/layouts/shared/components/profile_pic_menu.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/profile_pic_menu.js +41 -6
- package/dist/components/layouts/shared/components/profile_pic_menu_wrapper.d.ts +4 -2
- package/dist/components/layouts/shared/components/profile_pic_menu_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/profile_pic_menu_wrapper.js +3 -3
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +2 -2
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts +3 -0
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts.map +1 -1
- package/dist/components/layouts/shared/hooks/use_auth_status.js +4 -0
- package/dist/lib/auth_utility_config.server.js +2 -2
- package/dist/lib/services/user_profiles_cache.d.ts +76 -0
- package/dist/lib/services/user_profiles_cache.d.ts.map +1 -0
- package/dist/lib/services/user_profiles_cache.js +141 -0
- package/dist/lib/services/user_profiles_service.d.ts +17 -0
- package/dist/lib/services/user_profiles_service.d.ts.map +1 -1
- package/dist/lib/services/user_profiles_service.js +136 -44
- package/dist/lib/user_profiles_config.server.d.ts +15 -0
- package/dist/lib/user_profiles_config.server.d.ts.map +1 -0
- package/dist/lib/user_profiles_config.server.js +23 -0
- package/hazo_auth_config.example.ini +25 -5
- package/package.json +5 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { UserProfileInfo } from "./user_profiles_service";
|
|
2
|
+
/**
|
|
3
|
+
* LRU cache implementation with TTL and size limits for user profiles
|
|
4
|
+
* Uses Map to maintain insertion order for LRU eviction
|
|
5
|
+
*/
|
|
6
|
+
declare class UserProfilesCache {
|
|
7
|
+
private cache;
|
|
8
|
+
private max_size;
|
|
9
|
+
private ttl_ms;
|
|
10
|
+
constructor(max_size: number, ttl_minutes: number);
|
|
11
|
+
/**
|
|
12
|
+
* Gets a cached profile for a user
|
|
13
|
+
* Returns undefined if not found or expired
|
|
14
|
+
* @param user_id - User ID to look up
|
|
15
|
+
* @returns Profile or undefined
|
|
16
|
+
*/
|
|
17
|
+
get(user_id: string): UserProfileInfo | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Gets multiple profiles from cache
|
|
20
|
+
* Returns object with found profiles and missing IDs
|
|
21
|
+
* @param user_ids - Array of user IDs to look up
|
|
22
|
+
* @returns Object with cached profiles and IDs not in cache
|
|
23
|
+
*/
|
|
24
|
+
get_many(user_ids: string[]): {
|
|
25
|
+
cached: UserProfileInfo[];
|
|
26
|
+
missing_ids: string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Sets a cache entry for a user profile
|
|
30
|
+
* Evicts least recently used entries if cache is full
|
|
31
|
+
* @param user_id - User ID
|
|
32
|
+
* @param profile - User profile data
|
|
33
|
+
*/
|
|
34
|
+
set(user_id: string, profile: UserProfileInfo): void;
|
|
35
|
+
/**
|
|
36
|
+
* Sets multiple cache entries at once
|
|
37
|
+
* @param profiles - Array of user profiles to cache
|
|
38
|
+
*/
|
|
39
|
+
set_many(profiles: UserProfileInfo[]): void;
|
|
40
|
+
/**
|
|
41
|
+
* Invalidates cache for a specific user
|
|
42
|
+
* @param user_id - User ID to invalidate
|
|
43
|
+
*/
|
|
44
|
+
invalidate_user(user_id: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Invalidates cache for multiple users
|
|
47
|
+
* @param user_ids - Array of user IDs to invalidate
|
|
48
|
+
*/
|
|
49
|
+
invalidate_users(user_ids: string[]): void;
|
|
50
|
+
/**
|
|
51
|
+
* Invalidates all cache entries
|
|
52
|
+
*/
|
|
53
|
+
invalidate_all(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Gets cache statistics
|
|
56
|
+
* @returns Object with cache size and max size
|
|
57
|
+
*/
|
|
58
|
+
get_stats(): {
|
|
59
|
+
size: number;
|
|
60
|
+
max_size: number;
|
|
61
|
+
ttl_minutes: number;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Gets or creates the global user profiles cache instance
|
|
66
|
+
* @param max_size - Maximum cache size (default: 5000)
|
|
67
|
+
* @param ttl_minutes - TTL in minutes (default: 5)
|
|
68
|
+
* @returns User profiles cache instance
|
|
69
|
+
*/
|
|
70
|
+
export declare function get_user_profiles_cache(max_size?: number, ttl_minutes?: number): UserProfilesCache;
|
|
71
|
+
/**
|
|
72
|
+
* Resets the global cache instance (useful for testing)
|
|
73
|
+
*/
|
|
74
|
+
export declare function reset_user_profiles_cache(): void;
|
|
75
|
+
export {};
|
|
76
|
+
//# sourceMappingURL=user_profiles_cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user_profiles_cache.d.ts","sourceRoot":"","sources":["../../../src/lib/services/user_profiles_cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAc/D;;;GAGG;AACH,cAAM,iBAAiB;IACrB,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;gBAEX,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;IAMjD;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAuBjD;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG;QAC5B,MAAM,EAAE,eAAe,EAAE,CAAC;QAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB;IAgBD;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAmBpD;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,IAAI;IAM3C;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAItC;;;OAGG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAM1C;;OAEG;IACH,cAAc,IAAI,IAAI;IAItB;;;OAGG;IACH,SAAS,IAAI;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;KACrB;CAOF;AAMD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,GAAE,MAAa,EACvB,WAAW,GAAE,MAAU,GACtB,iBAAiB,CAKnB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// section: cache_class
|
|
2
|
+
/**
|
|
3
|
+
* LRU cache implementation with TTL and size limits for user profiles
|
|
4
|
+
* Uses Map to maintain insertion order for LRU eviction
|
|
5
|
+
*/
|
|
6
|
+
class UserProfilesCache {
|
|
7
|
+
constructor(max_size, ttl_minutes) {
|
|
8
|
+
this.cache = new Map();
|
|
9
|
+
this.max_size = max_size;
|
|
10
|
+
this.ttl_ms = ttl_minutes * 60 * 1000;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Gets a cached profile for a user
|
|
14
|
+
* Returns undefined if not found or expired
|
|
15
|
+
* @param user_id - User ID to look up
|
|
16
|
+
* @returns Profile or undefined
|
|
17
|
+
*/
|
|
18
|
+
get(user_id) {
|
|
19
|
+
const entry = this.cache.get(user_id);
|
|
20
|
+
if (!entry) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
const age = now - entry.timestamp;
|
|
25
|
+
// Check if entry is expired
|
|
26
|
+
if (age > this.ttl_ms) {
|
|
27
|
+
this.cache.delete(user_id);
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
// Move to end (most recently used)
|
|
31
|
+
this.cache.delete(user_id);
|
|
32
|
+
this.cache.set(user_id, entry);
|
|
33
|
+
return entry.profile;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Gets multiple profiles from cache
|
|
37
|
+
* Returns object with found profiles and missing IDs
|
|
38
|
+
* @param user_ids - Array of user IDs to look up
|
|
39
|
+
* @returns Object with cached profiles and IDs not in cache
|
|
40
|
+
*/
|
|
41
|
+
get_many(user_ids) {
|
|
42
|
+
const cached = [];
|
|
43
|
+
const missing_ids = [];
|
|
44
|
+
for (const user_id of user_ids) {
|
|
45
|
+
const profile = this.get(user_id);
|
|
46
|
+
if (profile) {
|
|
47
|
+
cached.push(profile);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
missing_ids.push(user_id);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { cached, missing_ids };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Sets a cache entry for a user profile
|
|
57
|
+
* Evicts least recently used entries if cache is full
|
|
58
|
+
* @param user_id - User ID
|
|
59
|
+
* @param profile - User profile data
|
|
60
|
+
*/
|
|
61
|
+
set(user_id, profile) {
|
|
62
|
+
// Evict LRU entries if cache is full
|
|
63
|
+
while (this.cache.size >= this.max_size) {
|
|
64
|
+
const first_key = this.cache.keys().next().value;
|
|
65
|
+
if (first_key) {
|
|
66
|
+
this.cache.delete(first_key);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const entry = {
|
|
73
|
+
profile,
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
};
|
|
76
|
+
this.cache.set(user_id, entry);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Sets multiple cache entries at once
|
|
80
|
+
* @param profiles - Array of user profiles to cache
|
|
81
|
+
*/
|
|
82
|
+
set_many(profiles) {
|
|
83
|
+
for (const profile of profiles) {
|
|
84
|
+
this.set(profile.user_id, profile);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Invalidates cache for a specific user
|
|
89
|
+
* @param user_id - User ID to invalidate
|
|
90
|
+
*/
|
|
91
|
+
invalidate_user(user_id) {
|
|
92
|
+
this.cache.delete(user_id);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Invalidates cache for multiple users
|
|
96
|
+
* @param user_ids - Array of user IDs to invalidate
|
|
97
|
+
*/
|
|
98
|
+
invalidate_users(user_ids) {
|
|
99
|
+
for (const user_id of user_ids) {
|
|
100
|
+
this.cache.delete(user_id);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Invalidates all cache entries
|
|
105
|
+
*/
|
|
106
|
+
invalidate_all() {
|
|
107
|
+
this.cache.clear();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Gets cache statistics
|
|
111
|
+
* @returns Object with cache size and max size
|
|
112
|
+
*/
|
|
113
|
+
get_stats() {
|
|
114
|
+
return {
|
|
115
|
+
size: this.cache.size,
|
|
116
|
+
max_size: this.max_size,
|
|
117
|
+
ttl_minutes: this.ttl_ms / 60000,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// section: singleton
|
|
122
|
+
// Global cache instance (initialized with defaults, will be configured on first use)
|
|
123
|
+
let user_profiles_cache_instance = null;
|
|
124
|
+
/**
|
|
125
|
+
* Gets or creates the global user profiles cache instance
|
|
126
|
+
* @param max_size - Maximum cache size (default: 5000)
|
|
127
|
+
* @param ttl_minutes - TTL in minutes (default: 5)
|
|
128
|
+
* @returns User profiles cache instance
|
|
129
|
+
*/
|
|
130
|
+
export function get_user_profiles_cache(max_size = 5000, ttl_minutes = 5) {
|
|
131
|
+
if (!user_profiles_cache_instance) {
|
|
132
|
+
user_profiles_cache_instance = new UserProfilesCache(max_size, ttl_minutes);
|
|
133
|
+
}
|
|
134
|
+
return user_profiles_cache_instance;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Resets the global cache instance (useful for testing)
|
|
138
|
+
*/
|
|
139
|
+
export function reset_user_profiles_cache() {
|
|
140
|
+
user_profiles_cache_instance = null;
|
|
141
|
+
}
|
|
@@ -19,13 +19,30 @@ export type GetProfilesResult = {
|
|
|
19
19
|
profiles: UserProfileInfo[];
|
|
20
20
|
not_found_ids: string[];
|
|
21
21
|
error?: string;
|
|
22
|
+
cache_stats?: {
|
|
23
|
+
hits: number;
|
|
24
|
+
misses: number;
|
|
25
|
+
cache_enabled: boolean;
|
|
26
|
+
};
|
|
22
27
|
};
|
|
23
28
|
/**
|
|
24
29
|
* Retrieves basic profile information for multiple users in a single batch call
|
|
25
30
|
* Useful for chat applications and similar use cases where basic user info is needed
|
|
31
|
+
* Uses LRU cache with configurable TTL for performance (default: 5 minutes)
|
|
26
32
|
* @param adapter - The hazo_connect adapter instance
|
|
27
33
|
* @param user_ids - Array of user IDs to retrieve profiles for
|
|
28
34
|
* @returns GetProfilesResult with found profiles and list of not found IDs
|
|
29
35
|
*/
|
|
30
36
|
export declare function hazo_get_user_profiles(adapter: HazoConnectAdapter, user_ids: string[]): Promise<GetProfilesResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Invalidates cache for specific user IDs
|
|
39
|
+
* Call this after user profile updates to ensure fresh data on next fetch
|
|
40
|
+
* @param user_ids - Array of user IDs to invalidate from cache
|
|
41
|
+
*/
|
|
42
|
+
export declare function invalidate_user_profiles_cache(user_ids: string[]): void;
|
|
43
|
+
/**
|
|
44
|
+
* Invalidates the entire user profiles cache
|
|
45
|
+
* Use sparingly - prefer invalidating specific users when possible
|
|
46
|
+
*/
|
|
47
|
+
export declare function invalidate_all_user_profiles_cache(): void;
|
|
31
48
|
//# sourceMappingURL=user_profiles_service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user_profiles_service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/user_profiles_service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"user_profiles_service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/user_profiles_service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AASvD;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;CACH,CAAC;AAuDF;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,iBAAiB,CAAC,CA4H5B;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAWvE;AAED;;;GAGG;AACH,wBAAgB,kCAAkC,IAAI,IAAI,CAWzD"}
|
|
@@ -2,16 +2,60 @@ import { createCrudService } from "hazo_connect/server";
|
|
|
2
2
|
import { differenceInDays } from "date-fns";
|
|
3
3
|
import { create_app_logger } from "../app_logger";
|
|
4
4
|
import { sanitize_error_for_user } from "../utils/error_sanitizer";
|
|
5
|
+
import { get_user_profiles_cache } from "./user_profiles_cache";
|
|
6
|
+
import { get_user_profiles_cache_config } from "../user_profiles_config.server";
|
|
5
7
|
// section: helpers
|
|
8
|
+
/**
|
|
9
|
+
* Fetches profiles from database for given user IDs
|
|
10
|
+
* @param adapter - The hazo_connect adapter instance
|
|
11
|
+
* @param user_ids - Array of user IDs to fetch
|
|
12
|
+
* @returns Object with profiles and not found IDs
|
|
13
|
+
*/
|
|
14
|
+
async function fetch_profiles_from_db(adapter, user_ids) {
|
|
15
|
+
const users_service = createCrudService(adapter, "hazo_users");
|
|
16
|
+
// Query users by IDs using the 'in' filter
|
|
17
|
+
// PostgREST supports 'in' filter syntax: id=in.(id1,id2,id3)
|
|
18
|
+
const users = await users_service.findBy({
|
|
19
|
+
id: `in.(${user_ids.join(",")})`,
|
|
20
|
+
});
|
|
21
|
+
// Handle case where no users are found
|
|
22
|
+
if (!Array.isArray(users)) {
|
|
23
|
+
return {
|
|
24
|
+
profiles: [],
|
|
25
|
+
not_found_ids: user_ids,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// Build set of found user IDs for quick lookup
|
|
29
|
+
const found_user_ids = new Set(users.map((user) => user.id));
|
|
30
|
+
// Determine which user IDs were not found
|
|
31
|
+
const not_found_ids = user_ids.filter((id) => !found_user_ids.has(id));
|
|
32
|
+
// Transform database records to UserProfileInfo
|
|
33
|
+
const now = new Date();
|
|
34
|
+
const profiles = users.map((user) => {
|
|
35
|
+
const created_at = user.created_at;
|
|
36
|
+
const created_date = new Date(created_at);
|
|
37
|
+
const days_since_created = differenceInDays(now, created_date);
|
|
38
|
+
return {
|
|
39
|
+
user_id: user.id,
|
|
40
|
+
profile_picture_url: user.profile_picture_url || null,
|
|
41
|
+
email: user.email_address,
|
|
42
|
+
name: user.name || null,
|
|
43
|
+
days_since_created,
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
return { profiles, not_found_ids };
|
|
47
|
+
}
|
|
6
48
|
/**
|
|
7
49
|
* Retrieves basic profile information for multiple users in a single batch call
|
|
8
50
|
* Useful for chat applications and similar use cases where basic user info is needed
|
|
51
|
+
* Uses LRU cache with configurable TTL for performance (default: 5 minutes)
|
|
9
52
|
* @param adapter - The hazo_connect adapter instance
|
|
10
53
|
* @param user_ids - Array of user IDs to retrieve profiles for
|
|
11
54
|
* @returns GetProfilesResult with found profiles and list of not found IDs
|
|
12
55
|
*/
|
|
13
56
|
export async function hazo_get_user_profiles(adapter, user_ids) {
|
|
14
57
|
const logger = create_app_logger();
|
|
58
|
+
const config = get_user_profiles_cache_config();
|
|
15
59
|
try {
|
|
16
60
|
// Handle empty input
|
|
17
61
|
if (!user_ids || user_ids.length === 0) {
|
|
@@ -19,62 +63,85 @@ export async function hazo_get_user_profiles(adapter, user_ids) {
|
|
|
19
63
|
success: true,
|
|
20
64
|
profiles: [],
|
|
21
65
|
not_found_ids: [],
|
|
66
|
+
cache_stats: {
|
|
67
|
+
hits: 0,
|
|
68
|
+
misses: 0,
|
|
69
|
+
cache_enabled: config.cache_enabled,
|
|
70
|
+
},
|
|
22
71
|
};
|
|
23
72
|
}
|
|
24
73
|
// Remove duplicates from input
|
|
25
74
|
const unique_user_ids = [...new Set(user_ids)];
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
75
|
+
// Initialize variables for cache tracking
|
|
76
|
+
let cache_hits = 0;
|
|
77
|
+
let cache_misses = 0;
|
|
78
|
+
let all_profiles = [];
|
|
79
|
+
let all_not_found_ids = [];
|
|
80
|
+
if (config.cache_enabled) {
|
|
81
|
+
// Get cache instance with config settings
|
|
82
|
+
const cache = get_user_profiles_cache(config.cache_max_entries, config.cache_ttl_minutes);
|
|
83
|
+
// Check cache first
|
|
84
|
+
const { cached, missing_ids } = cache.get_many(unique_user_ids);
|
|
85
|
+
cache_hits = cached.length;
|
|
86
|
+
cache_misses = missing_ids.length;
|
|
87
|
+
// If all profiles were cached, return immediately
|
|
88
|
+
if (missing_ids.length === 0) {
|
|
89
|
+
logger.info("hazo_get_user_profiles_cache_hit_all", {
|
|
90
|
+
filename: "user_profiles_service.ts",
|
|
91
|
+
line_number: 130,
|
|
92
|
+
message: "All profiles served from cache",
|
|
93
|
+
requested_count: unique_user_ids.length,
|
|
94
|
+
cache_hits,
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
success: true,
|
|
98
|
+
profiles: cached,
|
|
99
|
+
not_found_ids: [],
|
|
100
|
+
cache_stats: {
|
|
101
|
+
hits: cache_hits,
|
|
102
|
+
misses: 0,
|
|
103
|
+
cache_enabled: true,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Fetch missing profiles from database
|
|
108
|
+
const db_result = await fetch_profiles_from_db(adapter, missing_ids);
|
|
109
|
+
// Cache the newly fetched profiles
|
|
110
|
+
if (db_result.profiles.length > 0) {
|
|
111
|
+
cache.set_many(db_result.profiles);
|
|
112
|
+
}
|
|
113
|
+
// Combine cached and freshly fetched profiles
|
|
114
|
+
all_profiles = [...cached, ...db_result.profiles];
|
|
115
|
+
all_not_found_ids = db_result.not_found_ids;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Cache disabled - fetch all from database
|
|
119
|
+
cache_misses = unique_user_ids.length;
|
|
120
|
+
const db_result = await fetch_profiles_from_db(adapter, unique_user_ids);
|
|
121
|
+
all_profiles = db_result.profiles;
|
|
122
|
+
all_not_found_ids = db_result.not_found_ids;
|
|
46
123
|
}
|
|
47
|
-
// Build set of found user IDs for quick lookup
|
|
48
|
-
const found_user_ids = new Set(users.map((user) => user.id));
|
|
49
|
-
// Determine which user IDs were not found
|
|
50
|
-
const not_found_ids = unique_user_ids.filter((id) => !found_user_ids.has(id));
|
|
51
|
-
// Transform database records to UserProfileInfo
|
|
52
|
-
const now = new Date();
|
|
53
|
-
const profiles = users.map((user) => {
|
|
54
|
-
const created_at = user.created_at;
|
|
55
|
-
const created_date = new Date(created_at);
|
|
56
|
-
const days_since_created = differenceInDays(now, created_date);
|
|
57
|
-
return {
|
|
58
|
-
user_id: user.id,
|
|
59
|
-
profile_picture_url: user.profile_picture_url || null,
|
|
60
|
-
email: user.email_address,
|
|
61
|
-
name: user.name || null,
|
|
62
|
-
days_since_created,
|
|
63
|
-
};
|
|
64
|
-
});
|
|
65
124
|
// Log successful retrieval
|
|
66
125
|
logger.info("hazo_get_user_profiles_success", {
|
|
67
126
|
filename: "user_profiles_service.ts",
|
|
68
|
-
line_number:
|
|
127
|
+
line_number: 170,
|
|
69
128
|
message: "Successfully retrieved user profiles",
|
|
70
129
|
requested_count: unique_user_ids.length,
|
|
71
|
-
found_count:
|
|
72
|
-
not_found_count:
|
|
130
|
+
found_count: all_profiles.length,
|
|
131
|
+
not_found_count: all_not_found_ids.length,
|
|
132
|
+
cache_hits,
|
|
133
|
+
cache_misses,
|
|
134
|
+
cache_enabled: config.cache_enabled,
|
|
73
135
|
});
|
|
74
136
|
return {
|
|
75
137
|
success: true,
|
|
76
|
-
profiles,
|
|
77
|
-
not_found_ids,
|
|
138
|
+
profiles: all_profiles,
|
|
139
|
+
not_found_ids: all_not_found_ids,
|
|
140
|
+
cache_stats: {
|
|
141
|
+
hits: cache_hits,
|
|
142
|
+
misses: cache_misses,
|
|
143
|
+
cache_enabled: config.cache_enabled,
|
|
144
|
+
},
|
|
78
145
|
};
|
|
79
146
|
}
|
|
80
147
|
catch (error) {
|
|
@@ -84,7 +151,7 @@ export async function hazo_get_user_profiles(adapter, user_ids) {
|
|
|
84
151
|
logger,
|
|
85
152
|
context: {
|
|
86
153
|
filename: "user_profiles_service.ts",
|
|
87
|
-
line_number:
|
|
154
|
+
line_number: 195,
|
|
88
155
|
operation: "hazo_get_user_profiles",
|
|
89
156
|
user_ids_count: (user_ids === null || user_ids === void 0 ? void 0 : user_ids.length) || 0,
|
|
90
157
|
},
|
|
@@ -97,3 +164,28 @@ export async function hazo_get_user_profiles(adapter, user_ids) {
|
|
|
97
164
|
};
|
|
98
165
|
}
|
|
99
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Invalidates cache for specific user IDs
|
|
169
|
+
* Call this after user profile updates to ensure fresh data on next fetch
|
|
170
|
+
* @param user_ids - Array of user IDs to invalidate from cache
|
|
171
|
+
*/
|
|
172
|
+
export function invalidate_user_profiles_cache(user_ids) {
|
|
173
|
+
const config = get_user_profiles_cache_config();
|
|
174
|
+
if (!config.cache_enabled) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const cache = get_user_profiles_cache(config.cache_max_entries, config.cache_ttl_minutes);
|
|
178
|
+
cache.invalidate_users(user_ids);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Invalidates the entire user profiles cache
|
|
182
|
+
* Use sparingly - prefer invalidating specific users when possible
|
|
183
|
+
*/
|
|
184
|
+
export function invalidate_all_user_profiles_cache() {
|
|
185
|
+
const config = get_user_profiles_cache_config();
|
|
186
|
+
if (!config.cache_enabled) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const cache = get_user_profiles_cache(config.cache_max_entries, config.cache_ttl_minutes);
|
|
190
|
+
cache.invalidate_all();
|
|
191
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User profiles cache configuration options
|
|
3
|
+
*/
|
|
4
|
+
export type UserProfilesCacheConfig = {
|
|
5
|
+
cache_enabled: boolean;
|
|
6
|
+
cache_max_entries: number;
|
|
7
|
+
cache_ttl_minutes: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Reads user profiles cache configuration from hazo_auth_config.ini file
|
|
11
|
+
* Falls back to defaults if hazo_auth_config.ini is not found or section is missing
|
|
12
|
+
* @returns User profiles cache configuration options
|
|
13
|
+
*/
|
|
14
|
+
export declare function get_user_profiles_cache_config(): UserProfilesCacheConfig;
|
|
15
|
+
//# sourceMappingURL=user_profiles_config.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user_profiles_config.server.d.ts","sourceRoot":"","sources":["../../src/lib/user_profiles_config.server.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,uBAAuB,CA6BxE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// file_description: server-only helper to read user profiles cache configuration from hazo_auth_config.ini
|
|
2
|
+
// section: imports
|
|
3
|
+
import { get_config_number, } from "./config/config_loader.server";
|
|
4
|
+
// section: helpers
|
|
5
|
+
/**
|
|
6
|
+
* Reads user profiles cache configuration from hazo_auth_config.ini file
|
|
7
|
+
* Falls back to defaults if hazo_auth_config.ini is not found or section is missing
|
|
8
|
+
* @returns User profiles cache configuration options
|
|
9
|
+
*/
|
|
10
|
+
export function get_user_profiles_cache_config() {
|
|
11
|
+
const section_name = "hazo_auth__user_profiles";
|
|
12
|
+
// Cache settings
|
|
13
|
+
// cache_enabled: 0 = disabled, 1 = enabled (default: 1)
|
|
14
|
+
const cache_enabled_num = get_config_number(section_name, "cache_enabled", 1);
|
|
15
|
+
const cache_enabled = cache_enabled_num === 1;
|
|
16
|
+
const cache_max_entries = get_config_number(section_name, "cache_max_entries", 5000);
|
|
17
|
+
const cache_ttl_minutes = get_config_number(section_name, "cache_ttl_minutes", 5);
|
|
18
|
+
return {
|
|
19
|
+
cache_enabled,
|
|
20
|
+
cache_max_entries,
|
|
21
|
+
cache_ttl_minutes,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -305,17 +305,21 @@ enable_admin_ui = true
|
|
|
305
305
|
# default_super_user_email = admin@example.com
|
|
306
306
|
|
|
307
307
|
[hazo_auth__auth_utility]
|
|
308
|
-
# Authentication utility configuration
|
|
308
|
+
# Authentication utility configuration for hazo_get_auth()
|
|
309
|
+
# This function retrieves the current user, their permissions, and role IDs from cookies
|
|
310
|
+
# Results are cached in memory to improve performance on repeated calls
|
|
309
311
|
|
|
310
312
|
# Cache settings
|
|
311
313
|
# Maximum number of users to cache (LRU eviction, default: 10000)
|
|
312
314
|
# cache_max_users = 10000
|
|
313
315
|
|
|
314
|
-
# Cache TTL in minutes (default:
|
|
315
|
-
#
|
|
316
|
+
# Cache TTL in minutes - how long cached auth data is valid (default: 5)
|
|
317
|
+
# After TTL expires, the next call will fetch fresh data from database
|
|
318
|
+
# cache_ttl_minutes = 5
|
|
316
319
|
|
|
317
|
-
# Force cache refresh if older than this many minutes (default:
|
|
318
|
-
#
|
|
320
|
+
# Force cache refresh if older than this many minutes (default: 10)
|
|
321
|
+
# This is the hard limit - entries older than this are always refreshed
|
|
322
|
+
# cache_max_age_minutes = 10
|
|
319
323
|
|
|
320
324
|
# Rate limiting for /api/auth/get_auth endpoint
|
|
321
325
|
# Per-user rate limit (requests per minute, default: 100)
|
|
@@ -336,6 +340,22 @@ enable_admin_ui = true
|
|
|
336
340
|
# Example: admin_user_management:You don't have access to user management,admin_role_management:You don't have access to role management
|
|
337
341
|
# permission_error_messages =
|
|
338
342
|
|
|
343
|
+
[hazo_auth__user_profiles]
|
|
344
|
+
# User profiles cache configuration for hazo_get_user_profiles()
|
|
345
|
+
# This function retrieves basic profile info (name, email, profile picture) for multiple users
|
|
346
|
+
# Used by chat applications and similar use cases where batch user info is needed
|
|
347
|
+
# Results are cached in memory to improve performance on repeated calls
|
|
348
|
+
|
|
349
|
+
# Enable caching (1 = enabled, 0 = disabled, default: 1)
|
|
350
|
+
# cache_enabled = 1
|
|
351
|
+
|
|
352
|
+
# Maximum number of user profiles to cache (LRU eviction, default: 5000)
|
|
353
|
+
# cache_max_entries = 5000
|
|
354
|
+
|
|
355
|
+
# Cache TTL in minutes - how long cached profile data is valid (default: 5)
|
|
356
|
+
# After TTL expires, the next call will fetch fresh data from database
|
|
357
|
+
# cache_ttl_minutes = 5
|
|
358
|
+
|
|
339
359
|
[hazo_auth__profile_pic_menu]
|
|
340
360
|
# Profile picture menu configuration
|
|
341
361
|
# This component can be used in navbar or sidebar to show user profile picture or sign up/sign in buttons
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hazo_auth",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
13
|
"import": "./dist/index.js"
|
|
14
14
|
},
|
|
15
|
+
"./client": {
|
|
16
|
+
"types": "./dist/client.d.ts",
|
|
17
|
+
"import": "./dist/client.js"
|
|
18
|
+
},
|
|
15
19
|
"./components/layouts/login": {
|
|
16
20
|
"types": "./dist/components/layouts/login/index.d.ts",
|
|
17
21
|
"import": "./dist/components/layouts/login/index.js"
|