@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.
@@ -1,3 +1,4 @@
1
+ import { UpfilesClient } from '@thetechfossil/upfiles';
1
2
  import React, { ReactNode } from 'react';
2
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
4
 
@@ -64,6 +65,15 @@ interface AuthConfig {
64
65
  localStorageKey?: string;
65
66
  token?: string;
66
67
  csrfEnabled?: boolean;
68
+ upfilesConfig?: UpfilesConfig;
69
+ }
70
+ interface UpfilesConfig {
71
+ baseUrl: string;
72
+ apiKey?: string;
73
+ apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
74
+ presignUrl?: string;
75
+ presignPath?: string;
76
+ folderPath?: string;
67
77
  }
68
78
  interface Session {
69
79
  id: string;
@@ -109,6 +119,7 @@ interface UseAuthReturn {
109
119
  refreshCsrfToken: () => Promise<void>;
110
120
  changePassword: (oldPassword: string, newPassword: string) => Promise<AuthResponse>;
111
121
  updateAvatar: (avatar: string) => Promise<AuthResponse>;
122
+ uploadAndUpdateAvatar: (file: File) => Promise<AuthResponse>;
112
123
  requestEmailChange: (newEmail: string) => Promise<AuthResponse>;
113
124
  verifyEmailChange: (token: string) => Promise<AuthResponse>;
114
125
  generate2FA: () => Promise<MFASetup>;
@@ -127,6 +138,7 @@ declare class AuthService {
127
138
  private httpClient;
128
139
  private config;
129
140
  private token;
141
+ private upfilesClient;
130
142
  constructor(config: AuthConfig);
131
143
  private loadTokenFromStorage;
132
144
  private saveTokenToStorage;
@@ -152,6 +164,8 @@ declare class AuthService {
152
164
  resetPassword(token: string, password: string): Promise<AuthResponse>;
153
165
  changePassword(oldPassword: string, newPassword: string): Promise<AuthResponse>;
154
166
  updateAvatar(avatar: string): Promise<AuthResponse>;
167
+ uploadAndUpdateAvatar(file: File): Promise<AuthResponse>;
168
+ getUpfilesClient(): UpfilesClient | null;
155
169
  requestEmailChange(newEmail: string): Promise<AuthResponse>;
156
170
  verifyEmailChange(token: string): Promise<AuthResponse>;
157
171
  generate2FA(): Promise<{
@@ -213,6 +227,7 @@ interface UseAuthReturnBase {
213
227
  getProfile: () => Promise<User>;
214
228
  getAllUsers: () => Promise<User[]>;
215
229
  getUserById: (id: string) => Promise<User>;
230
+ uploadAndUpdateAvatar: (file: File) => Promise<AuthResponse>;
216
231
  }
217
232
  declare const useAuth$1: (config: AuthConfig) => UseAuthReturnBase;
218
233
 
@@ -235,6 +250,7 @@ interface AuthContextValue {
235
250
  resetPassword: (token: string, password: string) => Promise<AuthResponse>;
236
251
  changePassword: (oldPassword: string, newPassword: string) => Promise<AuthResponse>;
237
252
  updateAvatar: (avatar: string) => Promise<AuthResponse>;
253
+ uploadAndUpdateAvatar: (file: File) => Promise<AuthResponse>;
238
254
  requestEmailChange: (newEmail: string) => Promise<AuthResponse>;
239
255
  verifyEmailChange: (token: string) => Promise<AuthResponse>;
240
256
  generate2FA: () => Promise<any>;
@@ -424,6 +440,15 @@ interface UserProfileProps {
424
440
  showAvatar?: boolean;
425
441
  showEmailChange?: boolean;
426
442
  showPasswordChange?: boolean;
443
+ upfilesConfig?: {
444
+ baseUrl: string;
445
+ apiKey?: string;
446
+ apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
447
+ presignUrl?: string;
448
+ presignPath?: string;
449
+ folderPath?: string;
450
+ projectId?: string;
451
+ };
427
452
  }
428
453
  declare const UserProfile: React.FC<UserProfileProps>;
429
454
 
@@ -438,6 +463,50 @@ interface PhoneInputProps {
438
463
  }
439
464
  declare const PhoneInput: React.FC<PhoneInputProps>;
440
465
 
466
+ interface AvatarUploaderProps {
467
+ onUploadComplete?: (avatarUrl: string) => void;
468
+ onError?: (error: Error) => void;
469
+ className?: string;
470
+ buttonClassName?: string;
471
+ dropzoneClassName?: string;
472
+ maxFileSize?: number;
473
+ accept?: string[];
474
+ upfilesConfig: {
475
+ baseUrl: string;
476
+ apiKey?: string;
477
+ apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
478
+ presignUrl?: string;
479
+ presignPath?: string;
480
+ folderPath?: string;
481
+ projectId?: string;
482
+ };
483
+ buttonText?: string;
484
+ }
485
+ declare const AvatarUploader: React.FC<AvatarUploaderProps>;
486
+
487
+ interface AvatarManagerProps {
488
+ open: boolean;
489
+ onOpenChange: (open: boolean) => void;
490
+ onAvatarUpdated?: (avatarUrl: string) => void;
491
+ onError?: (error: Error) => void;
492
+ title?: string;
493
+ description?: string;
494
+ className?: string;
495
+ gridClassName?: string;
496
+ maxFileSize?: number;
497
+ mode?: 'full' | 'browse' | 'upload';
498
+ showDelete?: boolean;
499
+ upfilesConfig: {
500
+ baseUrl: string;
501
+ apiKey?: string;
502
+ apiKeyHeader?: 'authorization' | 'x-api-key' | 'x-up-api-key';
503
+ presignUrl?: string;
504
+ presignPath?: string;
505
+ folderPath?: string;
506
+ };
507
+ }
508
+ declare const AvatarManager: React.FC<AvatarManagerProps>;
509
+
441
510
  interface UseNextAuthReturn {
442
511
  user: User | null;
443
512
  isAuthenticated: boolean;
@@ -458,4 +527,4 @@ interface UseNextAuthReturn {
458
527
  }
459
528
  declare const useNextAuth: (config: AuthConfig) => UseNextAuthReturn;
460
529
 
461
- export { AuditLog, AuthConfig, AuthFlow, AuthProvider, AuthResponse, AuthService, AuthThemeProvider, ChangePassword, CsrfTokenResponse, EmailVerificationPage, ForgotPassword, HttpClient, LinkedAccount, LoginData, LoginForm, MFASetup, OAuthConfig, OAuthProvider, OtpForm, PhoneInput, ProtectedRoute, PublicRoute, RegisterData, RegisterForm, RegisterFormProps, ResetPassword, Session, SignIn, SignOut, SignUp, UpdateUserData, UseAuthReturn, User, UserButton, UserProfile, VerifyData, VerifyEmail, useAuth, useAuth$1 as useAuthLegacy, useAuthTheme, useNextAuth };
530
+ export { AuditLog, AuthConfig, AuthFlow, AuthProvider, AuthResponse, AuthService, AuthThemeProvider, AvatarManager, AvatarManagerProps, AvatarUploader, AvatarUploaderProps, ChangePassword, CsrfTokenResponse, EmailVerificationPage, ForgotPassword, HttpClient, LinkedAccount, LoginData, LoginForm, MFASetup, OAuthConfig, OAuthProvider, OtpForm, PhoneInput, ProtectedRoute, PublicRoute, RegisterData, RegisterForm, RegisterFormProps, ResetPassword, Session, SignIn, SignOut, SignUp, UpdateUserData, UpfilesConfig, UseAuthReturn, User, UserButton, UserProfile, VerifyData, VerifyEmail, useAuth, useAuth$1 as useAuthLegacy, useAuthTheme, useNextAuth };
@@ -1,7 +1,7 @@
1
- "use client";
2
1
  'use strict';
3
2
 
4
3
  var axios = require('axios');
4
+ var upfiles = require('@thetechfossil/upfiles');
5
5
  var React3 = require('react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var PhoneInputWithCountry = require('react-phone-number-input');
@@ -120,11 +120,10 @@ var HttpClient = class {
120
120
  }
121
121
  }
122
122
  };
123
-
124
- // src/core/auth-service.ts
125
123
  var AuthService = class {
126
124
  constructor(config) {
127
125
  this.token = null;
126
+ this.upfilesClient = null;
128
127
  this.config = {
129
128
  localStorageKey: "auth_token",
130
129
  csrfEnabled: true,
@@ -132,6 +131,15 @@ var AuthService = class {
132
131
  };
133
132
  this.httpClient = new HttpClient(this.config.baseUrl);
134
133
  this.loadTokenFromStorage();
134
+ if (this.config.upfilesConfig) {
135
+ this.upfilesClient = new upfiles.UpfilesClient({
136
+ baseUrl: this.config.upfilesConfig.baseUrl,
137
+ apiKey: this.config.upfilesConfig.apiKey,
138
+ apiKeyHeader: this.config.upfilesConfig.apiKeyHeader,
139
+ presignUrl: this.config.upfilesConfig.presignUrl,
140
+ presignPath: this.config.upfilesConfig.presignPath
141
+ });
142
+ }
135
143
  if (typeof window !== "undefined") {
136
144
  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;
137
145
  if (frontendBaseUrl) {
@@ -359,6 +367,28 @@ var AuthService = class {
359
367
  }
360
368
  return response;
361
369
  }
370
+ async uploadAndUpdateAvatar(file) {
371
+ if (!this.token) {
372
+ throw new Error("Not authenticated");
373
+ }
374
+ if (!this.upfilesClient) {
375
+ throw new Error("Upfiles configuration is required. Please provide upfilesConfig in AuthConfig.");
376
+ }
377
+ try {
378
+ const folderPath = this.config.upfilesConfig?.folderPath || "avatars/";
379
+ const uploadResult = await this.upfilesClient.upload(file, {
380
+ folderPath,
381
+ fetchThumbnails: true
382
+ });
383
+ const response = await this.updateAvatar(uploadResult.publicUrl);
384
+ return response;
385
+ } catch (error) {
386
+ throw new Error(`Failed to upload avatar: ${error.message || "Unknown error"}`);
387
+ }
388
+ }
389
+ getUpfilesClient() {
390
+ return this.upfilesClient;
391
+ }
362
392
  async requestEmailChange(newEmail) {
363
393
  if (!this.token) {
364
394
  throw new Error("Not authenticated");
@@ -589,6 +619,18 @@ var useAuth = (config) => {
589
619
  setLoading(false);
590
620
  }
591
621
  }, [authService]);
622
+ const uploadAndUpdateAvatar = React3.useCallback(async (file) => {
623
+ setLoading(true);
624
+ try {
625
+ const response = await authService.uploadAndUpdateAvatar(file);
626
+ if (response.success && response.user) {
627
+ setUser(response.user);
628
+ }
629
+ return response;
630
+ } finally {
631
+ setLoading(false);
632
+ }
633
+ }, [authService]);
592
634
  return {
593
635
  user,
594
636
  isAuthenticated,
@@ -601,7 +643,8 @@ var useAuth = (config) => {
601
643
  updateProfile,
602
644
  getProfile,
603
645
  getAllUsers,
604
- getUserById
646
+ getUserById,
647
+ uploadAndUpdateAvatar
605
648
  };
606
649
  };
607
650
  var ThemeContext = React3.createContext({ theme: "light", mounted: false });
@@ -660,7 +703,8 @@ var AuthProvider = ({ children, config }) => {
660
703
  const authConfig = {
661
704
  baseUrl: config?.baseUrl || (typeof window !== "undefined" ? process.env.NEXT_PUBLIC_AUTH_API_URL || process.env.REACT_APP_AUTH_API_URL || "http://localhost:7000" : "http://localhost:7000"),
662
705
  localStorageKey: config?.localStorageKey || "auth_token",
663
- csrfEnabled: config?.csrfEnabled !== void 0 ? config.csrfEnabled : true
706
+ csrfEnabled: config?.csrfEnabled !== void 0 ? config.csrfEnabled : true,
707
+ upfilesConfig: config?.upfilesConfig
664
708
  };
665
709
  const [authService] = React3.useState(() => new AuthService(authConfig));
666
710
  const [user, setUser] = React3.useState(null);
@@ -810,6 +854,18 @@ var AuthProvider = ({ children, config }) => {
810
854
  setLoading(false);
811
855
  }
812
856
  }, [authService]);
857
+ const uploadAndUpdateAvatar = React3.useCallback(async (file) => {
858
+ setLoading(true);
859
+ try {
860
+ const response = await authService.uploadAndUpdateAvatar(file);
861
+ if (response.success && response.user) {
862
+ setUser(response.user);
863
+ }
864
+ return response;
865
+ } finally {
866
+ setLoading(false);
867
+ }
868
+ }, [authService]);
813
869
  const requestEmailChange = React3.useCallback(async (newEmail) => {
814
870
  setLoading(true);
815
871
  try {
@@ -907,6 +963,7 @@ var AuthProvider = ({ children, config }) => {
907
963
  resetPassword,
908
964
  changePassword,
909
965
  updateAvatar,
966
+ uploadAndUpdateAvatar,
910
967
  requestEmailChange,
911
968
  verifyEmailChange,
912
969
  generate2FA,
@@ -4379,15 +4436,85 @@ var ChangePassword = ({ onSuccess, appearance }) => {
4379
4436
  )
4380
4437
  ] }) });
4381
4438
  };
4439
+ var AvatarUploader = ({
4440
+ onUploadComplete,
4441
+ onError,
4442
+ className,
4443
+ buttonClassName,
4444
+ maxFileSize = 5 * 1024 * 1024,
4445
+ // 5MB default
4446
+ upfilesConfig,
4447
+ buttonText = "Upload Avatar"
4448
+ }) => {
4449
+ const { uploadAndUpdateAvatar } = useAuth2();
4450
+ const [open, setOpen] = React3.useState(false);
4451
+ const [uploading, setUploading] = React3.useState(false);
4452
+ const handleSelect = async (image) => {
4453
+ setUploading(true);
4454
+ try {
4455
+ const response = await fetch(image.url);
4456
+ const blob = await response.blob();
4457
+ const file = new File([blob], image.originalName, { type: image.contentType });
4458
+ const result = await uploadAndUpdateAvatar(file);
4459
+ if (result.success && result.user?.avatar) {
4460
+ onUploadComplete?.(result.user.avatar);
4461
+ setOpen(false);
4462
+ } else {
4463
+ throw new Error(result.message || "Failed to update avatar");
4464
+ }
4465
+ } catch (error) {
4466
+ const err = error instanceof Error ? error : new Error("Upload failed");
4467
+ onError?.(err);
4468
+ } finally {
4469
+ setUploading(false);
4470
+ }
4471
+ };
4472
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
4473
+ /* @__PURE__ */ jsxRuntime.jsx(
4474
+ "button",
4475
+ {
4476
+ type: "button",
4477
+ onClick: () => setOpen(true),
4478
+ disabled: uploading,
4479
+ className: buttonClassName || "px-4 py-2 text-sm rounded border bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50",
4480
+ children: uploading ? "Uploading..." : buttonText
4481
+ }
4482
+ ),
4483
+ /* @__PURE__ */ jsxRuntime.jsx(
4484
+ upfiles.ImageManager,
4485
+ {
4486
+ open,
4487
+ onOpenChange: setOpen,
4488
+ clientOptions: {
4489
+ baseUrl: upfilesConfig.baseUrl,
4490
+ apiKey: upfilesConfig.apiKey,
4491
+ apiKeyHeader: upfilesConfig.apiKeyHeader || "authorization",
4492
+ presignUrl: upfilesConfig.presignUrl,
4493
+ presignPath: upfilesConfig.presignPath
4494
+ },
4495
+ projectId: upfilesConfig.projectId,
4496
+ folderPath: upfilesConfig.folderPath || "avatars/",
4497
+ title: "Select Avatar",
4498
+ description: "Upload a new avatar or select from existing images.",
4499
+ mode: "full",
4500
+ maxFileSize,
4501
+ maxFiles: 1,
4502
+ autoRecordToDb: true,
4503
+ fetchThumbnails: true,
4504
+ onSelect: handleSelect
4505
+ }
4506
+ )
4507
+ ] });
4508
+ };
4382
4509
  var UserProfile = ({
4383
4510
  showAvatar = true,
4384
4511
  showEmailChange = true,
4385
- showPasswordChange = true
4512
+ showPasswordChange = true,
4513
+ upfilesConfig
4386
4514
  }) => {
4387
4515
  const { user, updateProfile, requestEmailChange } = useAuth2();
4388
4516
  const colors = useThemeColors();
4389
4517
  const [name, setName] = React3.useState(user?.name || "");
4390
- const [avatar, setAvatar] = React3.useState(user?.avatar || "");
4391
4518
  const [phoneNumber, setPhoneNumber] = React3.useState(user?.phoneNumber || "");
4392
4519
  const [newEmail, setNewEmail] = React3.useState("");
4393
4520
  const [isLoading, setIsLoading] = React3.useState(false);
@@ -4403,9 +4530,6 @@ var UserProfile = ({
4403
4530
  if (name !== user?.name) {
4404
4531
  updates.name = name;
4405
4532
  }
4406
- if (showAvatar && avatar !== user?.avatar) {
4407
- updates.avatar = avatar;
4408
- }
4409
4533
  if (phoneNumber !== user?.phoneNumber) {
4410
4534
  updates.phoneNumber = phoneNumber;
4411
4535
  }
@@ -4426,6 +4550,12 @@ var UserProfile = ({
4426
4550
  setIsLoading(false);
4427
4551
  }
4428
4552
  };
4553
+ const handleAvatarUploadComplete = (avatarUrl) => {
4554
+ setSuccess("Avatar updated successfully!");
4555
+ };
4556
+ const handleAvatarUploadError = (error2) => {
4557
+ setError(error2.message || "Failed to upload avatar");
4558
+ };
4429
4559
  const handleRequestEmailChange = async (e) => {
4430
4560
  e.preventDefault();
4431
4561
  setIsLoading(true);
@@ -4528,34 +4658,36 @@ var UserProfile = ({
4528
4658
  }
4529
4659
  )
4530
4660
  ] }),
4531
- showAvatar && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "20px" }, children: [
4532
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "avatar", style: {
4661
+ showAvatar && upfilesConfig && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "20px" }, children: [
4662
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: {
4533
4663
  display: "block",
4534
4664
  marginBottom: "8px",
4535
4665
  fontWeight: 500,
4536
4666
  color: colors.textSecondary,
4537
4667
  fontSize: "14px"
4538
- }, children: "Avatar URL" }),
4539
- /* @__PURE__ */ jsxRuntime.jsx(
4540
- "input",
4668
+ }, children: "Avatar" }),
4669
+ user?.avatar && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "12px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
4670
+ "img",
4541
4671
  {
4542
- id: "avatar",
4543
- type: "url",
4544
- value: avatar,
4545
- onChange: (e) => setAvatar(e.target.value),
4546
- disabled: isLoading,
4672
+ src: user.avatar,
4673
+ alt: "Current avatar",
4547
4674
  style: {
4548
- width: "100%",
4549
- padding: "12px 16px",
4550
- border: `1px solid ${colors.borderSecondary}`,
4551
- borderRadius: "8px",
4552
- fontSize: "16px",
4553
- boxSizing: "border-box",
4554
- backgroundColor: colors.bgSecondary,
4555
- color: colors.textPrimary,
4556
- transition: "all 0.2s ease"
4557
- },
4558
- placeholder: "https://example.com/avatar.jpg"
4675
+ width: "80px",
4676
+ height: "80px",
4677
+ borderRadius: "50%",
4678
+ objectFit: "cover",
4679
+ border: `2px solid ${colors.borderSecondary}`
4680
+ }
4681
+ }
4682
+ ) }),
4683
+ /* @__PURE__ */ jsxRuntime.jsx(
4684
+ AvatarUploader,
4685
+ {
4686
+ upfilesConfig,
4687
+ onUploadComplete: handleAvatarUploadComplete,
4688
+ onError: handleAvatarUploadError,
4689
+ maxFileSize: 5 * 1024 * 1024,
4690
+ accept: ["image/*"]
4559
4691
  }
4560
4692
  )
4561
4693
  ] }),
@@ -4667,6 +4799,65 @@ var UserProfile = ({
4667
4799
  ] })
4668
4800
  ] });
4669
4801
  };
4802
+ var AvatarManager = ({
4803
+ open,
4804
+ onOpenChange,
4805
+ onAvatarUpdated,
4806
+ onError,
4807
+ title = "Select Avatar",
4808
+ description = "Choose an existing image or upload a new one",
4809
+ className,
4810
+ gridClassName,
4811
+ maxFileSize = 5 * 1024 * 1024,
4812
+ // 5MB default
4813
+ mode = "full",
4814
+ showDelete = false,
4815
+ upfilesConfig
4816
+ }) => {
4817
+ const { updateProfile } = useAuth2();
4818
+ const [updating, setUpdating] = React3.useState(false);
4819
+ const handleSelect = async (image) => {
4820
+ setUpdating(true);
4821
+ try {
4822
+ const response = await updateProfile({ avatar: image.url });
4823
+ if (response.success && response.user?.avatar) {
4824
+ onAvatarUpdated?.(response.user.avatar);
4825
+ onOpenChange(false);
4826
+ } else {
4827
+ throw new Error(response.message || "Failed to update avatar");
4828
+ }
4829
+ } catch (error) {
4830
+ const err = error instanceof Error ? error : new Error("Failed to update avatar");
4831
+ onError?.(err);
4832
+ } finally {
4833
+ setUpdating(false);
4834
+ }
4835
+ };
4836
+ return /* @__PURE__ */ jsxRuntime.jsx(
4837
+ upfiles.ImageManager,
4838
+ {
4839
+ open,
4840
+ onOpenChange,
4841
+ clientOptions: {
4842
+ baseUrl: upfilesConfig.baseUrl,
4843
+ apiKey: upfilesConfig.apiKey,
4844
+ apiKeyHeader: upfilesConfig.apiKeyHeader || "authorization",
4845
+ presignUrl: upfilesConfig.presignUrl,
4846
+ presignPath: upfilesConfig.presignPath
4847
+ },
4848
+ folderPath: upfilesConfig.folderPath || "avatars/",
4849
+ title,
4850
+ description,
4851
+ className,
4852
+ gridClassName,
4853
+ onSelect: handleSelect,
4854
+ maxFileSize,
4855
+ mode,
4856
+ showDelete,
4857
+ fetchThumbnails: true
4858
+ }
4859
+ );
4860
+ };
4670
4861
  var isServer = typeof window === "undefined";
4671
4862
  var useNextAuth = (config) => {
4672
4863
  const [authService] = React3.useState(() => {
@@ -4863,6 +5054,8 @@ exports.AuthFlow = AuthFlow;
4863
5054
  exports.AuthProvider = AuthProvider;
4864
5055
  exports.AuthService = AuthService;
4865
5056
  exports.AuthThemeProvider = AuthThemeProvider;
5057
+ exports.AvatarManager = AvatarManager;
5058
+ exports.AvatarUploader = AvatarUploader;
4866
5059
  exports.ChangePassword = ChangePassword;
4867
5060
  exports.EmailVerificationPage = EmailVerificationPage;
4868
5061
  exports.ForgotPassword = ForgotPassword;