hazo_auth 4.5.0 → 4.5.1

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.
@@ -14,6 +14,7 @@ export declare function GET(request: NextRequest): Promise<NextResponse<{
14
14
  label: string;
15
15
  badge_color: string;
16
16
  }[];
17
+ multi_tenancy_enabled: boolean;
17
18
  users: {
18
19
  id: unknown;
19
20
  name: {} | null;
@@ -25,10 +26,12 @@ export declare function GET(request: NextRequest): Promise<NextResponse<{
25
26
  profile_picture_url: {} | null;
26
27
  profile_source: {} | null;
27
28
  user_type: string | null;
29
+ org_id: string | null | undefined;
30
+ root_org_id: string | null | undefined;
28
31
  }[];
29
32
  }>>;
30
33
  /**
31
- * PATCH - Update user (deactivate: set is_active to false)
34
+ * PATCH - Update user (deactivate: set is_active to false, assign org, etc.)
32
35
  */
33
36
  export declare function PATCH(request: NextRequest): Promise<NextResponse<{
34
37
  error: string;
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/app/api/hazo_auth/user_management/users/route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAexD,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAGvC;;;GAGG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;IAwE7C;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,WAAW;;;;IA2G/C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;IA2E9C"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/app/api/hazo_auth/user_management/users/route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsBxD,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAGvC;;;GAGG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;IA+E7C;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,WAAW;;;;IA2L/C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;IA2E9C"}
@@ -9,6 +9,10 @@ import { request_password_reset } from "../../../../../lib/services/password_res
9
9
  import { get_auth_cache } from "../../../../../lib/auth/auth_cache";
10
10
  import { get_auth_utility_config } from "../../../../../lib/auth_utility_config.server";
11
11
  import { is_user_types_enabled, get_all_user_types, get_user_types_config, } from "../../../../../lib/user_types_config.server";
12
+ import { is_multi_tenancy_enabled } from "../../../../../lib/multi_tenancy_config.server";
13
+ import { get_org_by_id, can_add_user_to_org, } from "../../../../../lib/services/org_service";
14
+ import { get_org_cache } from "../../../../../lib/auth/org_cache";
15
+ import { get_multi_tenancy_config } from "../../../../../lib/multi_tenancy_config.server";
12
16
  // section: route_config
13
17
  export const dynamic = 'force-dynamic';
14
18
  // section: api_handler
@@ -42,10 +46,13 @@ export async function GET(request) {
42
46
  badge_color: t.badge_color,
43
47
  }))
44
48
  : [];
49
+ // Check if multi-tenancy is enabled
50
+ const multi_tenancy_enabled = is_multi_tenancy_enabled();
45
51
  return NextResponse.json({
46
52
  success: true,
47
53
  user_types_enabled,
48
54
  available_user_types,
55
+ multi_tenancy_enabled,
49
56
  users: users.map((user) => ({
50
57
  id: user.id,
51
58
  name: user.name || null,
@@ -57,6 +64,9 @@ export async function GET(request) {
57
64
  profile_picture_url: user.profile_picture_url || null,
58
65
  profile_source: user.profile_source || null,
59
66
  user_type: user.user_type || null,
67
+ // Include org info when multi-tenancy is enabled
68
+ org_id: multi_tenancy_enabled ? user.org_id || null : undefined,
69
+ root_org_id: multi_tenancy_enabled ? user.root_org_id || null : undefined,
60
70
  })),
61
71
  }, { status: 200 });
62
72
  }
@@ -73,13 +83,13 @@ export async function GET(request) {
73
83
  }
74
84
  }
75
85
  /**
76
- * PATCH - Update user (deactivate: set is_active to false)
86
+ * PATCH - Update user (deactivate: set is_active to false, assign org, etc.)
77
87
  */
78
88
  export async function PATCH(request) {
79
89
  const logger = create_app_logger();
80
90
  try {
81
91
  const body = await request.json();
82
- const { user_id, is_active, user_type } = body;
92
+ const { user_id, is_active, user_type, org_id } = body;
83
93
  // user_id is always required
84
94
  if (!user_id) {
85
95
  return NextResponse.json({ error: "user_id is required" }, { status: 400 });
@@ -88,6 +98,7 @@ export async function PATCH(request) {
88
98
  const update_data = {
89
99
  changed_at: new Date().toISOString(),
90
100
  };
101
+ const hazoConnect = get_hazo_connect_instance();
91
102
  // Handle is_active if provided
92
103
  if (typeof is_active === "boolean") {
93
104
  update_data.is_active = is_active;
@@ -108,20 +119,53 @@ export async function PATCH(request) {
108
119
  }
109
120
  }
110
121
  }
122
+ // Handle org_id if provided (only when multi-tenancy is enabled)
123
+ if (org_id !== undefined) {
124
+ if (!is_multi_tenancy_enabled()) {
125
+ return NextResponse.json({ error: "Multi-tenancy is not enabled" }, { status: 400 });
126
+ }
127
+ // Allow null to clear the org assignment
128
+ if (org_id === null || org_id === "") {
129
+ update_data.org_id = null;
130
+ update_data.root_org_id = null;
131
+ }
132
+ else {
133
+ // Validate org exists
134
+ const org_result = await get_org_by_id(hazoConnect, org_id);
135
+ if (!org_result.success || !org_result.org) {
136
+ return NextResponse.json({ error: "Organization not found" }, { status: 400 });
137
+ }
138
+ const org = org_result.org;
139
+ // Check if org is active
140
+ if (org.active === false) {
141
+ return NextResponse.json({ error: "Cannot assign user to inactive organization" }, { status: 400 });
142
+ }
143
+ // Check user limit
144
+ const limit_check = await can_add_user_to_org(hazoConnect, org_id);
145
+ if (limit_check.success && !limit_check.can_add) {
146
+ return NextResponse.json({ error: limit_check.reason || "Organization user limit reached" }, { status: 400 });
147
+ }
148
+ // Set org_id and calculate root_org_id
149
+ update_data.org_id = org_id;
150
+ update_data.root_org_id = org.root_org_id || org_id;
151
+ }
152
+ }
111
153
  // Ensure there's something to update besides changed_at
112
154
  if (Object.keys(update_data).length === 1) {
113
155
  return NextResponse.json({ error: "No valid fields to update" }, { status: 400 });
114
156
  }
115
- const hazoConnect = get_hazo_connect_instance();
116
157
  const users_service = createCrudService(hazoConnect, "hazo_users");
117
158
  // Update user
118
159
  await users_service.updateById(user_id, update_data);
119
- // Invalidate user cache after deactivation
120
- if (is_active === false) {
160
+ // Invalidate caches
161
+ let cache_invalidated = false;
162
+ // Invalidate user auth cache if user deactivated or org changed
163
+ if (is_active === false || org_id !== undefined) {
121
164
  try {
122
- const config = get_auth_utility_config();
123
- const cache = get_auth_cache(config.cache_max_users, config.cache_ttl_minutes, config.cache_max_age_minutes);
124
- cache.invalidate_user(user_id);
165
+ const auth_config = get_auth_utility_config();
166
+ const auth_cache = get_auth_cache(auth_config.cache_max_users, auth_config.cache_ttl_minutes, auth_config.cache_max_age_minutes);
167
+ auth_cache.invalidate_user(user_id);
168
+ cache_invalidated = true;
125
169
  }
126
170
  catch (cache_error) {
127
171
  // Log but don't fail user update if cache invalidation fails
@@ -134,11 +178,34 @@ export async function PATCH(request) {
134
178
  });
135
179
  }
136
180
  }
181
+ // If org changed, also invalidate org cache for old and new orgs
182
+ if (org_id !== undefined && is_multi_tenancy_enabled()) {
183
+ try {
184
+ const mt_config = get_multi_tenancy_config();
185
+ const org_cache = get_org_cache(mt_config.org_cache_max_entries, mt_config.org_cache_ttl_minutes);
186
+ // Invalidate for the new org if set
187
+ if (org_id && typeof org_id === "string") {
188
+ org_cache.invalidate(org_id);
189
+ }
190
+ }
191
+ catch (cache_error) {
192
+ // Log but don't fail user update if cache invalidation fails
193
+ const cache_error_message = cache_error instanceof Error ? cache_error.message : "Unknown error";
194
+ logger.warn("user_management_org_cache_invalidation_failed", {
195
+ filename: get_filename(),
196
+ line_number: get_line_number(),
197
+ user_id,
198
+ org_id,
199
+ error: cache_error_message,
200
+ });
201
+ }
202
+ }
137
203
  logger.info("user_management_user_updated", {
138
204
  filename: get_filename(),
139
205
  line_number: get_line_number(),
140
206
  user_id,
141
207
  updated_fields: Object.keys(update_data).filter((k) => k !== "changed_at"),
208
+ cache_invalidated,
142
209
  });
143
210
  return NextResponse.json({ success: true }, { status: 200 });
144
211
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_auth",
3
- "version": "4.5.0",
3
+ "version": "4.5.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",