hazo_auth 1.6.0 → 1.6.2
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 +191 -19
- package/SETUP_CHECKLIST.md +190 -65
- package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts +9 -0
- package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.js +82 -0
- package/dist/app/api/hazo_auth/library_photos/route.d.ts +9 -0
- package/dist/app/api/hazo_auth/library_photos/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/library_photos/route.js +31 -6
- package/dist/cli/generate.d.ts +6 -1
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +101 -34
- package/dist/cli/index.js +64 -11
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +206 -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.js +2 -2
- package/dist/lib/auth_utility_config.server.js +2 -2
- package/dist/lib/services/profile_picture_service.d.ts +34 -2
- package/dist/lib/services/profile_picture_service.d.ts.map +1 -1
- package/dist/lib/services/profile_picture_service.js +157 -15
- 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/dist/page_components/forgot_password.d.ts +19 -0
- package/dist/page_components/forgot_password.d.ts.map +1 -0
- package/dist/page_components/forgot_password.js +36 -0
- package/dist/page_components/index.d.ts +7 -0
- package/dist/page_components/index.d.ts.map +1 -0
- package/dist/page_components/index.js +9 -0
- package/dist/page_components/login.d.ts +26 -0
- package/dist/page_components/login.d.ts.map +1 -0
- package/dist/page_components/login.js +40 -0
- package/dist/page_components/my_settings.d.ts +64 -0
- package/dist/page_components/my_settings.d.ts.map +1 -0
- package/dist/page_components/my_settings.js +67 -0
- package/dist/page_components/register.d.ts +25 -0
- package/dist/page_components/register.d.ts.map +1 -0
- package/dist/page_components/register.js +43 -0
- package/dist/page_components/reset_password.d.ts +25 -0
- package/dist/page_components/reset_password.d.ts.map +1 -0
- package/dist/page_components/reset_password.js +43 -0
- package/dist/page_components/verify_email.d.ts +21 -0
- package/dist/page_components/verify_email.d.ts.map +1 -0
- package/dist/page_components/verify_email.js +36 -0
- package/dist/server/routes/index.d.ts +1 -0
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +1 -0
- package/dist/server/routes/library_photo.d.ts +2 -0
- package/dist/server/routes/library_photo.d.ts.map +1 -0
- package/dist/server/routes/library_photo.js +3 -0
- package/hazo_auth_config.example.ini +25 -5
- package/package.json +33 -1
|
@@ -7,7 +7,52 @@ import { create_app_logger } from "../app_logger";
|
|
|
7
7
|
import path from "path";
|
|
8
8
|
import fs from "fs";
|
|
9
9
|
import { map_ui_source_to_db } from "./profile_picture_source_mapper";
|
|
10
|
+
// section: cache
|
|
11
|
+
// Cache the resolved library path to avoid repeated filesystem checks
|
|
12
|
+
let cached_library_path = null;
|
|
13
|
+
let cached_library_source = null;
|
|
10
14
|
// section: helpers
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the library path, checking project's public folder first, then node_modules
|
|
17
|
+
* @returns Object with path and source, or null if not found
|
|
18
|
+
*/
|
|
19
|
+
function resolve_library_path() {
|
|
20
|
+
// Return cached value if available
|
|
21
|
+
if (cached_library_path && cached_library_source) {
|
|
22
|
+
if (fs.existsSync(cached_library_path)) {
|
|
23
|
+
return { path: cached_library_path, source: cached_library_source };
|
|
24
|
+
}
|
|
25
|
+
// Cache is stale, clear it
|
|
26
|
+
cached_library_path = null;
|
|
27
|
+
cached_library_source = null;
|
|
28
|
+
}
|
|
29
|
+
const config = get_profile_picture_config();
|
|
30
|
+
const library_subpath = config.library_photo_path.replace(/^\//, "");
|
|
31
|
+
// Try 1: Project's public folder
|
|
32
|
+
const project_library_path = path.resolve(process.cwd(), "public", library_subpath);
|
|
33
|
+
if (fs.existsSync(project_library_path)) {
|
|
34
|
+
// Check if it has any content (not just empty directory)
|
|
35
|
+
try {
|
|
36
|
+
const entries = fs.readdirSync(project_library_path);
|
|
37
|
+
if (entries.length > 0) {
|
|
38
|
+
cached_library_path = project_library_path;
|
|
39
|
+
cached_library_source = "project";
|
|
40
|
+
return { path: project_library_path, source: "project" };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (_a) {
|
|
44
|
+
// Continue to fallback
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Try 2: node_modules/hazo_auth/public folder
|
|
48
|
+
const node_modules_library_path = path.resolve(process.cwd(), "node_modules", "hazo_auth", "public", library_subpath);
|
|
49
|
+
if (fs.existsSync(node_modules_library_path)) {
|
|
50
|
+
cached_library_path = node_modules_library_path;
|
|
51
|
+
cached_library_source = "node_modules";
|
|
52
|
+
return { path: node_modules_library_path, source: "node_modules" };
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
11
56
|
/**
|
|
12
57
|
* Generates Gravatar URL from email address
|
|
13
58
|
* @param email - User's email address
|
|
@@ -27,13 +72,12 @@ export function get_gravatar_url(email, size) {
|
|
|
27
72
|
* @returns Array of category names
|
|
28
73
|
*/
|
|
29
74
|
export function get_library_categories() {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
if (!fs.existsSync(library_path)) {
|
|
75
|
+
const resolved = resolve_library_path();
|
|
76
|
+
if (!resolved) {
|
|
33
77
|
return [];
|
|
34
78
|
}
|
|
35
79
|
try {
|
|
36
|
-
const entries = fs.readdirSync(
|
|
80
|
+
const entries = fs.readdirSync(resolved.path, { withFileTypes: true });
|
|
37
81
|
return entries
|
|
38
82
|
.filter((entry) => entry.isDirectory())
|
|
39
83
|
.map((entry) => entry.name)
|
|
@@ -45,37 +89,84 @@ export function get_library_categories() {
|
|
|
45
89
|
logger.warn("profile_picture_service_read_categories_failed", {
|
|
46
90
|
filename: "profile_picture_service.ts",
|
|
47
91
|
line_number: 0,
|
|
48
|
-
library_path,
|
|
92
|
+
library_path: resolved.path,
|
|
93
|
+
source: resolved.source,
|
|
49
94
|
error: error_message,
|
|
50
95
|
});
|
|
51
96
|
return [];
|
|
52
97
|
}
|
|
53
98
|
}
|
|
54
99
|
/**
|
|
55
|
-
* Gets photos in a specific library category
|
|
100
|
+
* Gets photos in a specific library category with pagination support
|
|
56
101
|
* @param category - Category name
|
|
57
|
-
* @
|
|
102
|
+
* @param page - Page number (1-indexed, default 1)
|
|
103
|
+
* @param page_size - Number of photos per page (default 20, max 100)
|
|
104
|
+
* @returns Object with photos array and pagination info
|
|
58
105
|
*/
|
|
59
|
-
export function
|
|
106
|
+
export function get_library_photos_paginated(category, page = 1, page_size = 20) {
|
|
107
|
+
const resolved = resolve_library_path();
|
|
60
108
|
const config = get_profile_picture_config();
|
|
61
|
-
|
|
109
|
+
// Ensure page_size is within bounds
|
|
110
|
+
const effective_page_size = Math.min(Math.max(1, page_size), 100);
|
|
111
|
+
const effective_page = Math.max(1, page);
|
|
112
|
+
if (!resolved) {
|
|
113
|
+
return {
|
|
114
|
+
photos: [],
|
|
115
|
+
total: 0,
|
|
116
|
+
page: effective_page,
|
|
117
|
+
page_size: effective_page_size,
|
|
118
|
+
has_more: false,
|
|
119
|
+
source: "project",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const category_path = path.join(resolved.path, category);
|
|
62
123
|
if (!fs.existsSync(category_path)) {
|
|
63
|
-
return
|
|
124
|
+
return {
|
|
125
|
+
photos: [],
|
|
126
|
+
total: 0,
|
|
127
|
+
page: effective_page,
|
|
128
|
+
page_size: effective_page_size,
|
|
129
|
+
has_more: false,
|
|
130
|
+
source: resolved.source,
|
|
131
|
+
};
|
|
64
132
|
}
|
|
65
133
|
try {
|
|
66
134
|
const fileTypes = get_file_types_config();
|
|
67
|
-
const allowedExtensions = fileTypes.allowed_image_extensions.map(ext => `.${ext.toLowerCase()}`);
|
|
135
|
+
const allowedExtensions = fileTypes.allowed_image_extensions.map(ext => ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`);
|
|
68
136
|
const entries = fs.readdirSync(category_path, { withFileTypes: true });
|
|
69
|
-
const
|
|
137
|
+
const all_photos = entries
|
|
70
138
|
.filter((entry) => {
|
|
71
139
|
if (!entry.isFile())
|
|
72
140
|
return false;
|
|
73
141
|
const ext = path.extname(entry.name).toLowerCase();
|
|
74
142
|
return allowedExtensions.includes(ext);
|
|
75
143
|
})
|
|
76
|
-
.map((entry) =>
|
|
144
|
+
.map((entry) => entry.name)
|
|
77
145
|
.sort();
|
|
78
|
-
|
|
146
|
+
const total = all_photos.length;
|
|
147
|
+
const start_index = (effective_page - 1) * effective_page_size;
|
|
148
|
+
const end_index = start_index + effective_page_size;
|
|
149
|
+
const page_photos = all_photos.slice(start_index, end_index);
|
|
150
|
+
// Generate URLs based on source
|
|
151
|
+
// For node_modules source, we need to serve via API route
|
|
152
|
+
const photo_urls = page_photos.map((filename) => {
|
|
153
|
+
if (resolved.source === "node_modules") {
|
|
154
|
+
// Serve via API route that reads from node_modules
|
|
155
|
+
return `/api/hazo_auth/library_photo/${category}/${filename}`;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// Serve directly from public folder
|
|
159
|
+
return `${config.library_photo_path}/${category}/${filename}`;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
photos: photo_urls,
|
|
164
|
+
total,
|
|
165
|
+
page: effective_page,
|
|
166
|
+
page_size: effective_page_size,
|
|
167
|
+
has_more: end_index < total,
|
|
168
|
+
source: resolved.source,
|
|
169
|
+
};
|
|
79
170
|
}
|
|
80
171
|
catch (error) {
|
|
81
172
|
const logger = create_app_logger();
|
|
@@ -85,10 +176,61 @@ export function get_library_photos(category) {
|
|
|
85
176
|
line_number: 0,
|
|
86
177
|
category,
|
|
87
178
|
category_path,
|
|
179
|
+
source: resolved.source,
|
|
88
180
|
error: error_message,
|
|
89
181
|
});
|
|
90
|
-
return
|
|
182
|
+
return {
|
|
183
|
+
photos: [],
|
|
184
|
+
total: 0,
|
|
185
|
+
page: effective_page,
|
|
186
|
+
page_size: effective_page_size,
|
|
187
|
+
has_more: false,
|
|
188
|
+
source: resolved.source,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Gets photos in a specific library category (legacy non-paginated version)
|
|
194
|
+
* @param category - Category name
|
|
195
|
+
* @returns Array of photo URLs (relative to public directory or API route)
|
|
196
|
+
*/
|
|
197
|
+
export function get_library_photos(category) {
|
|
198
|
+
// Use paginated version with large page size for backwards compatibility
|
|
199
|
+
const result = get_library_photos_paginated(category, 1, 1000);
|
|
200
|
+
return result.photos;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Gets the physical file path for a library photo (used for serving from node_modules)
|
|
204
|
+
* @param category - Category name
|
|
205
|
+
* @param filename - Photo filename
|
|
206
|
+
* @returns Full file path or null if not found
|
|
207
|
+
*/
|
|
208
|
+
export function get_library_photo_path(category, filename) {
|
|
209
|
+
const resolved = resolve_library_path();
|
|
210
|
+
if (!resolved) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const photo_path = path.join(resolved.path, category, filename);
|
|
214
|
+
if (fs.existsSync(photo_path)) {
|
|
215
|
+
return photo_path;
|
|
91
216
|
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Gets the source of library photos (for diagnostic purposes)
|
|
221
|
+
* @returns Source type or null if no library found
|
|
222
|
+
*/
|
|
223
|
+
export function get_library_source() {
|
|
224
|
+
var _a;
|
|
225
|
+
const resolved = resolve_library_path();
|
|
226
|
+
return (_a = resolved === null || resolved === void 0 ? void 0 : resolved.source) !== null && _a !== void 0 ? _a : null;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Clears the library path cache (useful for testing or after copying files)
|
|
230
|
+
*/
|
|
231
|
+
export function clear_library_cache() {
|
|
232
|
+
cached_library_path = null;
|
|
233
|
+
cached_library_source = null;
|
|
92
234
|
}
|
|
93
235
|
/**
|
|
94
236
|
* Gets default profile picture based on configuration priority
|
|
@@ -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"}
|