@thetechfossil/auth2 1.2.6 → 1.2.8

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.
@@ -24,6 +24,15 @@ interface AuthConfig {
24
24
  localStorageKey?: string;
25
25
  token?: string;
26
26
  csrfEnabled?: boolean;
27
+ upfilesConfig?: UpfilesConfig;
28
+ }
29
+ interface UpfilesConfig {
30
+ baseUrl: string;
31
+ apiKey?: string;
32
+ apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
33
+ presignUrl?: string;
34
+ presignPath?: string;
35
+ folderPath?: string;
27
36
  }
28
37
 
29
38
  interface RegisterFormProps {
@@ -168,6 +177,15 @@ interface UserProfileProps {
168
177
  showAvatar?: boolean;
169
178
  showEmailChange?: boolean;
170
179
  showPasswordChange?: boolean;
180
+ upfilesConfig?: {
181
+ baseUrl: string;
182
+ apiKey?: string;
183
+ apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
184
+ presignUrl?: string;
185
+ presignPath?: string;
186
+ folderPath?: string;
187
+ projectId?: string;
188
+ };
171
189
  }
172
190
  declare const UserProfile: React.FC<UserProfileProps>;
173
191
 
@@ -182,4 +200,48 @@ interface PhoneInputProps {
182
200
  }
183
201
  declare const PhoneInput: React.FC<PhoneInputProps>;
184
202
 
185
- export { AuthFlow, ChangePassword, EmailVerificationPage, ForgotPassword, LoginForm, OtpForm, PhoneInput, ProtectedRoute, PublicRoute, RegisterForm, RegisterFormProps, ResetPassword, SignIn, SignOut, SignUp, UserButton, UserProfile, VerifyEmail };
203
+ interface AvatarUploaderProps {
204
+ onUploadComplete?: (avatarUrl: string) => void;
205
+ onError?: (error: Error) => void;
206
+ className?: string;
207
+ buttonClassName?: string;
208
+ dropzoneClassName?: string;
209
+ maxFileSize?: number;
210
+ accept?: string[];
211
+ upfilesConfig: {
212
+ baseUrl: string;
213
+ apiKey?: string;
214
+ apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
215
+ presignUrl?: string;
216
+ presignPath?: string;
217
+ folderPath?: string;
218
+ projectId?: string;
219
+ };
220
+ buttonText?: string;
221
+ }
222
+ declare const AvatarUploader: React.FC<AvatarUploaderProps>;
223
+
224
+ interface AvatarManagerProps {
225
+ open: boolean;
226
+ onOpenChange: (open: boolean) => void;
227
+ onAvatarUpdated?: (avatarUrl: string) => void;
228
+ onError?: (error: Error) => void;
229
+ title?: string;
230
+ description?: string;
231
+ className?: string;
232
+ gridClassName?: string;
233
+ maxFileSize?: number;
234
+ mode?: 'full' | 'browse' | 'upload';
235
+ showDelete?: boolean;
236
+ upfilesConfig: {
237
+ baseUrl: string;
238
+ apiKey?: string;
239
+ apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
240
+ presignUrl?: string;
241
+ presignPath?: string;
242
+ folderPath?: string;
243
+ };
244
+ }
245
+ declare const AvatarManager: React.FC<AvatarManagerProps>;
246
+
247
+ export { AuthFlow, AvatarManager, AvatarManagerProps, AvatarUploader, AvatarUploaderProps, ChangePassword, EmailVerificationPage, ForgotPassword, LoginForm, OtpForm, PhoneInput, ProtectedRoute, PublicRoute, RegisterForm, RegisterFormProps, ResetPassword, SignIn, SignOut, SignUp, UserButton, UserProfile, VerifyEmail };
@@ -1,8 +1,8 @@
1
- "use client";
2
1
  'use strict';
3
2
 
4
3
  var React2 = require('react');
5
4
  var axios = require('axios');
5
+ var upfiles = require('@thetechfossil/upfiles');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var PhoneInputWithCountry = require('react-phone-number-input');
8
8
  require('react-phone-number-input/style.css');
@@ -119,11 +119,10 @@ var HttpClient = class {
119
119
  }
120
120
  }
121
121
  };
122
-
123
- // src/core/auth-service.ts
124
122
  var AuthService = class {
125
123
  constructor(config) {
126
124
  this.token = null;
125
+ this.upfilesClient = null;
127
126
  this.config = {
128
127
  localStorageKey: "auth_token",
129
128
  csrfEnabled: true,
@@ -131,6 +130,15 @@ var AuthService = class {
131
130
  };
132
131
  this.httpClient = new HttpClient(this.config.baseUrl);
133
132
  this.loadTokenFromStorage();
133
+ if (this.config.upfilesConfig) {
134
+ this.upfilesClient = new upfiles.UpfilesClient({
135
+ baseUrl: this.config.upfilesConfig.baseUrl,
136
+ apiKey: this.config.upfilesConfig.apiKey,
137
+ apiKeyHeader: this.config.upfilesConfig.apiKeyHeader,
138
+ presignUrl: this.config.upfilesConfig.presignUrl,
139
+ presignPath: this.config.upfilesConfig.presignPath
140
+ });
141
+ }
134
142
  if (typeof window !== "undefined") {
135
143
  const frontendBaseUrl = process.env.NEXT_PUBLIC_FRONTEND_BASE_URL || process.env.REACT_APP_FRONTEND_BASE_URL || process.env.NEXT_PUBLIC_APP_URL || window.location.origin;
136
144
  if (frontendBaseUrl) {
@@ -358,6 +366,28 @@ var AuthService = class {
358
366
  }
359
367
  return response;
360
368
  }
369
+ async uploadAndUpdateAvatar(file) {
370
+ if (!this.token) {
371
+ throw new Error("Not authenticated");
372
+ }
373
+ if (!this.upfilesClient) {
374
+ throw new Error("Upfiles configuration is required. Please provide upfilesConfig in AuthConfig.");
375
+ }
376
+ try {
377
+ const folderPath = this.config.upfilesConfig?.folderPath || "avatars/";
378
+ const uploadResult = await this.upfilesClient.upload(file, {
379
+ folderPath,
380
+ fetchThumbnails: true
381
+ });
382
+ const response = await this.updateAvatar(uploadResult.publicUrl);
383
+ return response;
384
+ } catch (error) {
385
+ throw new Error(`Failed to upload avatar: ${error.message || "Unknown error"}`);
386
+ }
387
+ }
388
+ getUpfilesClient() {
389
+ return this.upfilesClient;
390
+ }
361
391
  async requestEmailChange(newEmail) {
362
392
  if (!this.token) {
363
393
  throw new Error("Not authenticated");
@@ -590,6 +620,18 @@ var useAuth = (config) => {
590
620
  setLoading(false);
591
621
  }
592
622
  }, [authService]);
623
+ const uploadAndUpdateAvatar = React2.useCallback(async (file) => {
624
+ setLoading(true);
625
+ try {
626
+ const response = await authService.uploadAndUpdateAvatar(file);
627
+ if (response.success && response.user) {
628
+ setUser(response.user);
629
+ }
630
+ return response;
631
+ } finally {
632
+ setLoading(false);
633
+ }
634
+ }, [authService]);
593
635
  return {
594
636
  user,
595
637
  isAuthenticated,
@@ -602,7 +644,8 @@ var useAuth = (config) => {
602
644
  updateProfile,
603
645
  getProfile,
604
646
  getAllUsers,
605
- getUserById
647
+ getUserById,
648
+ uploadAndUpdateAvatar
606
649
  };
607
650
  };
608
651
  var ThemeContext = React2.createContext({ theme: "light", mounted: false });
@@ -4069,15 +4112,85 @@ var ChangePassword = ({ onSuccess, appearance }) => {
4069
4112
  )
4070
4113
  ] }) });
4071
4114
  };
4115
+ var AvatarUploader = ({
4116
+ onUploadComplete,
4117
+ onError,
4118
+ className,
4119
+ buttonClassName,
4120
+ maxFileSize = 5 * 1024 * 1024,
4121
+ // 5MB default
4122
+ upfilesConfig,
4123
+ buttonText = "Upload Avatar"
4124
+ }) => {
4125
+ const { uploadAndUpdateAvatar } = useAuth2();
4126
+ const [open, setOpen] = React2.useState(false);
4127
+ const [uploading, setUploading] = React2.useState(false);
4128
+ const handleSelect = async (image) => {
4129
+ setUploading(true);
4130
+ try {
4131
+ const response = await fetch(image.url);
4132
+ const blob = await response.blob();
4133
+ const file = new File([blob], image.originalName, { type: image.contentType });
4134
+ const result = await uploadAndUpdateAvatar(file);
4135
+ if (result.success && result.user?.avatar) {
4136
+ onUploadComplete?.(result.user.avatar);
4137
+ setOpen(false);
4138
+ } else {
4139
+ throw new Error(result.message || "Failed to update avatar");
4140
+ }
4141
+ } catch (error) {
4142
+ const err = error instanceof Error ? error : new Error("Upload failed");
4143
+ onError?.(err);
4144
+ } finally {
4145
+ setUploading(false);
4146
+ }
4147
+ };
4148
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
4149
+ /* @__PURE__ */ jsxRuntime.jsx(
4150
+ "button",
4151
+ {
4152
+ type: "button",
4153
+ onClick: () => setOpen(true),
4154
+ disabled: uploading,
4155
+ className: buttonClassName || "px-4 py-2 text-sm rounded border bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50",
4156
+ children: uploading ? "Uploading..." : buttonText
4157
+ }
4158
+ ),
4159
+ /* @__PURE__ */ jsxRuntime.jsx(
4160
+ upfiles.ImageManager,
4161
+ {
4162
+ open,
4163
+ onOpenChange: setOpen,
4164
+ clientOptions: {
4165
+ baseUrl: upfilesConfig.baseUrl,
4166
+ apiKey: upfilesConfig.apiKey,
4167
+ apiKeyHeader: upfilesConfig.apiKeyHeader || "authorization",
4168
+ presignUrl: upfilesConfig.presignUrl,
4169
+ presignPath: upfilesConfig.presignPath
4170
+ },
4171
+ projectId: upfilesConfig.projectId,
4172
+ folderPath: upfilesConfig.folderPath || "avatars/",
4173
+ title: "Select Avatar",
4174
+ description: "Upload a new avatar or select from existing images.",
4175
+ mode: "full",
4176
+ maxFileSize,
4177
+ maxFiles: 1,
4178
+ autoRecordToDb: true,
4179
+ fetchThumbnails: true,
4180
+ onSelect: handleSelect
4181
+ }
4182
+ )
4183
+ ] });
4184
+ };
4072
4185
  var UserProfile = ({
4073
4186
  showAvatar = true,
4074
4187
  showEmailChange = true,
4075
- showPasswordChange = true
4188
+ showPasswordChange = true,
4189
+ upfilesConfig
4076
4190
  }) => {
4077
4191
  const { user, updateProfile, requestEmailChange } = useAuth2();
4078
4192
  const colors = useThemeColors();
4079
4193
  const [name, setName] = React2.useState(user?.name || "");
4080
- const [avatar, setAvatar] = React2.useState(user?.avatar || "");
4081
4194
  const [phoneNumber, setPhoneNumber] = React2.useState(user?.phoneNumber || "");
4082
4195
  const [newEmail, setNewEmail] = React2.useState("");
4083
4196
  const [isLoading, setIsLoading] = React2.useState(false);
@@ -4093,9 +4206,6 @@ var UserProfile = ({
4093
4206
  if (name !== user?.name) {
4094
4207
  updates.name = name;
4095
4208
  }
4096
- if (showAvatar && avatar !== user?.avatar) {
4097
- updates.avatar = avatar;
4098
- }
4099
4209
  if (phoneNumber !== user?.phoneNumber) {
4100
4210
  updates.phoneNumber = phoneNumber;
4101
4211
  }
@@ -4116,6 +4226,12 @@ var UserProfile = ({
4116
4226
  setIsLoading(false);
4117
4227
  }
4118
4228
  };
4229
+ const handleAvatarUploadComplete = (avatarUrl) => {
4230
+ setSuccess("Avatar updated successfully!");
4231
+ };
4232
+ const handleAvatarUploadError = (error2) => {
4233
+ setError(error2.message || "Failed to upload avatar");
4234
+ };
4119
4235
  const handleRequestEmailChange = async (e) => {
4120
4236
  e.preventDefault();
4121
4237
  setIsLoading(true);
@@ -4218,34 +4334,36 @@ var UserProfile = ({
4218
4334
  }
4219
4335
  )
4220
4336
  ] }),
4221
- showAvatar && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "20px" }, children: [
4222
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "avatar", style: {
4337
+ showAvatar && upfilesConfig && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "20px" }, children: [
4338
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: {
4223
4339
  display: "block",
4224
4340
  marginBottom: "8px",
4225
4341
  fontWeight: 500,
4226
4342
  color: colors.textSecondary,
4227
4343
  fontSize: "14px"
4228
- }, children: "Avatar URL" }),
4229
- /* @__PURE__ */ jsxRuntime.jsx(
4230
- "input",
4344
+ }, children: "Avatar" }),
4345
+ user?.avatar && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "12px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
4346
+ "img",
4231
4347
  {
4232
- id: "avatar",
4233
- type: "url",
4234
- value: avatar,
4235
- onChange: (e) => setAvatar(e.target.value),
4236
- disabled: isLoading,
4348
+ src: user.avatar,
4349
+ alt: "Current avatar",
4237
4350
  style: {
4238
- width: "100%",
4239
- padding: "12px 16px",
4240
- border: `1px solid ${colors.borderSecondary}`,
4241
- borderRadius: "8px",
4242
- fontSize: "16px",
4243
- boxSizing: "border-box",
4244
- backgroundColor: colors.bgSecondary,
4245
- color: colors.textPrimary,
4246
- transition: "all 0.2s ease"
4247
- },
4248
- placeholder: "https://example.com/avatar.jpg"
4351
+ width: "80px",
4352
+ height: "80px",
4353
+ borderRadius: "50%",
4354
+ objectFit: "cover",
4355
+ border: `2px solid ${colors.borderSecondary}`
4356
+ }
4357
+ }
4358
+ ) }),
4359
+ /* @__PURE__ */ jsxRuntime.jsx(
4360
+ AvatarUploader,
4361
+ {
4362
+ upfilesConfig,
4363
+ onUploadComplete: handleAvatarUploadComplete,
4364
+ onError: handleAvatarUploadError,
4365
+ maxFileSize: 5 * 1024 * 1024,
4366
+ accept: ["image/*"]
4249
4367
  }
4250
4368
  )
4251
4369
  ] }),
@@ -4357,8 +4475,69 @@ var UserProfile = ({
4357
4475
  ] })
4358
4476
  ] });
4359
4477
  };
4478
+ var AvatarManager = ({
4479
+ open,
4480
+ onOpenChange,
4481
+ onAvatarUpdated,
4482
+ onError,
4483
+ title = "Select Avatar",
4484
+ description = "Choose an existing image or upload a new one",
4485
+ className,
4486
+ gridClassName,
4487
+ maxFileSize = 5 * 1024 * 1024,
4488
+ // 5MB default
4489
+ mode = "full",
4490
+ showDelete = false,
4491
+ upfilesConfig
4492
+ }) => {
4493
+ const { updateProfile } = useAuth2();
4494
+ const [updating, setUpdating] = React2.useState(false);
4495
+ const handleSelect = async (image) => {
4496
+ setUpdating(true);
4497
+ try {
4498
+ const response = await updateProfile({ avatar: image.url });
4499
+ if (response.success && response.user?.avatar) {
4500
+ onAvatarUpdated?.(response.user.avatar);
4501
+ onOpenChange(false);
4502
+ } else {
4503
+ throw new Error(response.message || "Failed to update avatar");
4504
+ }
4505
+ } catch (error) {
4506
+ const err = error instanceof Error ? error : new Error("Failed to update avatar");
4507
+ onError?.(err);
4508
+ } finally {
4509
+ setUpdating(false);
4510
+ }
4511
+ };
4512
+ return /* @__PURE__ */ jsxRuntime.jsx(
4513
+ upfiles.ImageManager,
4514
+ {
4515
+ open,
4516
+ onOpenChange,
4517
+ clientOptions: {
4518
+ baseUrl: upfilesConfig.baseUrl,
4519
+ apiKey: upfilesConfig.apiKey,
4520
+ apiKeyHeader: upfilesConfig.apiKeyHeader || "authorization",
4521
+ presignUrl: upfilesConfig.presignUrl,
4522
+ presignPath: upfilesConfig.presignPath
4523
+ },
4524
+ folderPath: upfilesConfig.folderPath || "avatars/",
4525
+ title,
4526
+ description,
4527
+ className,
4528
+ gridClassName,
4529
+ onSelect: handleSelect,
4530
+ maxFileSize,
4531
+ mode,
4532
+ showDelete,
4533
+ fetchThumbnails: true
4534
+ }
4535
+ );
4536
+ };
4360
4537
 
4361
4538
  exports.AuthFlow = AuthFlow;
4539
+ exports.AvatarManager = AvatarManager;
4540
+ exports.AvatarUploader = AvatarUploader;
4362
4541
  exports.ChangePassword = ChangePassword;
4363
4542
  exports.EmailVerificationPage = EmailVerificationPage;
4364
4543
  exports.ForgotPassword = ForgotPassword;