@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,5 +1,5 @@
1
- "use client";
2
1
  import axios from 'axios';
2
+ import { UpfilesClient, ImageManager } from '@thetechfossil/upfiles';
3
3
  import React3, { createContext, forwardRef, useState, useCallback, useEffect, useContext, useMemo, useRef } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import PhoneInputWithCountry from 'react-phone-number-input';
@@ -112,11 +112,10 @@ var HttpClient = class {
112
112
  }
113
113
  }
114
114
  };
115
-
116
- // src/core/auth-service.ts
117
115
  var AuthService = class {
118
116
  constructor(config) {
119
117
  this.token = null;
118
+ this.upfilesClient = null;
120
119
  this.config = {
121
120
  localStorageKey: "auth_token",
122
121
  csrfEnabled: true,
@@ -124,6 +123,15 @@ var AuthService = class {
124
123
  };
125
124
  this.httpClient = new HttpClient(this.config.baseUrl);
126
125
  this.loadTokenFromStorage();
126
+ if (this.config.upfilesConfig) {
127
+ this.upfilesClient = new UpfilesClient({
128
+ baseUrl: this.config.upfilesConfig.baseUrl,
129
+ apiKey: this.config.upfilesConfig.apiKey,
130
+ apiKeyHeader: this.config.upfilesConfig.apiKeyHeader,
131
+ presignUrl: this.config.upfilesConfig.presignUrl,
132
+ presignPath: this.config.upfilesConfig.presignPath
133
+ });
134
+ }
127
135
  if (typeof window !== "undefined") {
128
136
  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;
129
137
  if (frontendBaseUrl) {
@@ -351,6 +359,28 @@ var AuthService = class {
351
359
  }
352
360
  return response;
353
361
  }
362
+ async uploadAndUpdateAvatar(file) {
363
+ if (!this.token) {
364
+ throw new Error("Not authenticated");
365
+ }
366
+ if (!this.upfilesClient) {
367
+ throw new Error("Upfiles configuration is required. Please provide upfilesConfig in AuthConfig.");
368
+ }
369
+ try {
370
+ const folderPath = this.config.upfilesConfig?.folderPath || "avatars/";
371
+ const uploadResult = await this.upfilesClient.upload(file, {
372
+ folderPath,
373
+ fetchThumbnails: true
374
+ });
375
+ const response = await this.updateAvatar(uploadResult.publicUrl);
376
+ return response;
377
+ } catch (error) {
378
+ throw new Error(`Failed to upload avatar: ${error.message || "Unknown error"}`);
379
+ }
380
+ }
381
+ getUpfilesClient() {
382
+ return this.upfilesClient;
383
+ }
354
384
  async requestEmailChange(newEmail) {
355
385
  if (!this.token) {
356
386
  throw new Error("Not authenticated");
@@ -581,6 +611,18 @@ var useAuth = (config) => {
581
611
  setLoading(false);
582
612
  }
583
613
  }, [authService]);
614
+ const uploadAndUpdateAvatar = useCallback(async (file) => {
615
+ setLoading(true);
616
+ try {
617
+ const response = await authService.uploadAndUpdateAvatar(file);
618
+ if (response.success && response.user) {
619
+ setUser(response.user);
620
+ }
621
+ return response;
622
+ } finally {
623
+ setLoading(false);
624
+ }
625
+ }, [authService]);
584
626
  return {
585
627
  user,
586
628
  isAuthenticated,
@@ -593,7 +635,8 @@ var useAuth = (config) => {
593
635
  updateProfile,
594
636
  getProfile,
595
637
  getAllUsers,
596
- getUserById
638
+ getUserById,
639
+ uploadAndUpdateAvatar
597
640
  };
598
641
  };
599
642
  var ThemeContext = createContext({ theme: "light", mounted: false });
@@ -652,7 +695,8 @@ var AuthProvider = ({ children, config }) => {
652
695
  const authConfig = {
653
696
  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"),
654
697
  localStorageKey: config?.localStorageKey || "auth_token",
655
- csrfEnabled: config?.csrfEnabled !== void 0 ? config.csrfEnabled : true
698
+ csrfEnabled: config?.csrfEnabled !== void 0 ? config.csrfEnabled : true,
699
+ upfilesConfig: config?.upfilesConfig
656
700
  };
657
701
  const [authService] = useState(() => new AuthService(authConfig));
658
702
  const [user, setUser] = useState(null);
@@ -802,6 +846,18 @@ var AuthProvider = ({ children, config }) => {
802
846
  setLoading(false);
803
847
  }
804
848
  }, [authService]);
849
+ const uploadAndUpdateAvatar = useCallback(async (file) => {
850
+ setLoading(true);
851
+ try {
852
+ const response = await authService.uploadAndUpdateAvatar(file);
853
+ if (response.success && response.user) {
854
+ setUser(response.user);
855
+ }
856
+ return response;
857
+ } finally {
858
+ setLoading(false);
859
+ }
860
+ }, [authService]);
805
861
  const requestEmailChange = useCallback(async (newEmail) => {
806
862
  setLoading(true);
807
863
  try {
@@ -899,6 +955,7 @@ var AuthProvider = ({ children, config }) => {
899
955
  resetPassword,
900
956
  changePassword,
901
957
  updateAvatar,
958
+ uploadAndUpdateAvatar,
902
959
  requestEmailChange,
903
960
  verifyEmailChange,
904
961
  generate2FA,
@@ -4371,15 +4428,85 @@ var ChangePassword = ({ onSuccess, appearance }) => {
4371
4428
  )
4372
4429
  ] }) });
4373
4430
  };
4431
+ var AvatarUploader = ({
4432
+ onUploadComplete,
4433
+ onError,
4434
+ className,
4435
+ buttonClassName,
4436
+ maxFileSize = 5 * 1024 * 1024,
4437
+ // 5MB default
4438
+ upfilesConfig,
4439
+ buttonText = "Upload Avatar"
4440
+ }) => {
4441
+ const { uploadAndUpdateAvatar } = useAuth2();
4442
+ const [open, setOpen] = useState(false);
4443
+ const [uploading, setUploading] = useState(false);
4444
+ const handleSelect = async (image) => {
4445
+ setUploading(true);
4446
+ try {
4447
+ const response = await fetch(image.url);
4448
+ const blob = await response.blob();
4449
+ const file = new File([blob], image.originalName, { type: image.contentType });
4450
+ const result = await uploadAndUpdateAvatar(file);
4451
+ if (result.success && result.user?.avatar) {
4452
+ onUploadComplete?.(result.user.avatar);
4453
+ setOpen(false);
4454
+ } else {
4455
+ throw new Error(result.message || "Failed to update avatar");
4456
+ }
4457
+ } catch (error) {
4458
+ const err = error instanceof Error ? error : new Error("Upload failed");
4459
+ onError?.(err);
4460
+ } finally {
4461
+ setUploading(false);
4462
+ }
4463
+ };
4464
+ return /* @__PURE__ */ jsxs("div", { className, children: [
4465
+ /* @__PURE__ */ jsx(
4466
+ "button",
4467
+ {
4468
+ type: "button",
4469
+ onClick: () => setOpen(true),
4470
+ disabled: uploading,
4471
+ className: buttonClassName || "px-4 py-2 text-sm rounded border bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50",
4472
+ children: uploading ? "Uploading..." : buttonText
4473
+ }
4474
+ ),
4475
+ /* @__PURE__ */ jsx(
4476
+ ImageManager,
4477
+ {
4478
+ open,
4479
+ onOpenChange: setOpen,
4480
+ clientOptions: {
4481
+ baseUrl: upfilesConfig.baseUrl,
4482
+ apiKey: upfilesConfig.apiKey,
4483
+ apiKeyHeader: upfilesConfig.apiKeyHeader || "authorization",
4484
+ presignUrl: upfilesConfig.presignUrl,
4485
+ presignPath: upfilesConfig.presignPath
4486
+ },
4487
+ projectId: upfilesConfig.projectId,
4488
+ folderPath: upfilesConfig.folderPath || "avatars/",
4489
+ title: "Select Avatar",
4490
+ description: "Upload a new avatar or select from existing images.",
4491
+ mode: "full",
4492
+ maxFileSize,
4493
+ maxFiles: 1,
4494
+ autoRecordToDb: true,
4495
+ fetchThumbnails: true,
4496
+ onSelect: handleSelect
4497
+ }
4498
+ )
4499
+ ] });
4500
+ };
4374
4501
  var UserProfile = ({
4375
4502
  showAvatar = true,
4376
4503
  showEmailChange = true,
4377
- showPasswordChange = true
4504
+ showPasswordChange = true,
4505
+ upfilesConfig
4378
4506
  }) => {
4379
4507
  const { user, updateProfile, requestEmailChange } = useAuth2();
4380
4508
  const colors = useThemeColors();
4381
4509
  const [name, setName] = useState(user?.name || "");
4382
- const [avatar, setAvatar] = useState(user?.avatar || "");
4383
4510
  const [phoneNumber, setPhoneNumber] = useState(user?.phoneNumber || "");
4384
4511
  const [newEmail, setNewEmail] = useState("");
4385
4512
  const [isLoading, setIsLoading] = useState(false);
@@ -4395,9 +4522,6 @@ var UserProfile = ({
4395
4522
  if (name !== user?.name) {
4396
4523
  updates.name = name;
4397
4524
  }
4398
- if (showAvatar && avatar !== user?.avatar) {
4399
- updates.avatar = avatar;
4400
- }
4401
4525
  if (phoneNumber !== user?.phoneNumber) {
4402
4526
  updates.phoneNumber = phoneNumber;
4403
4527
  }
@@ -4418,6 +4542,12 @@ var UserProfile = ({
4418
4542
  setIsLoading(false);
4419
4543
  }
4420
4544
  };
4545
+ const handleAvatarUploadComplete = (avatarUrl) => {
4546
+ setSuccess("Avatar updated successfully!");
4547
+ };
4548
+ const handleAvatarUploadError = (error2) => {
4549
+ setError(error2.message || "Failed to upload avatar");
4550
+ };
4421
4551
  const handleRequestEmailChange = async (e) => {
4422
4552
  e.preventDefault();
4423
4553
  setIsLoading(true);
@@ -4520,34 +4650,36 @@ var UserProfile = ({
4520
4650
  }
4521
4651
  )
4522
4652
  ] }),
4523
- showAvatar && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "20px" }, children: [
4524
- /* @__PURE__ */ jsx("label", { htmlFor: "avatar", style: {
4653
+ showAvatar && upfilesConfig && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "20px" }, children: [
4654
+ /* @__PURE__ */ jsx("label", { style: {
4525
4655
  display: "block",
4526
4656
  marginBottom: "8px",
4527
4657
  fontWeight: 500,
4528
4658
  color: colors.textSecondary,
4529
4659
  fontSize: "14px"
4530
- }, children: "Avatar URL" }),
4531
- /* @__PURE__ */ jsx(
4532
- "input",
4660
+ }, children: "Avatar" }),
4661
+ user?.avatar && /* @__PURE__ */ jsx("div", { style: { marginBottom: "12px" }, children: /* @__PURE__ */ jsx(
4662
+ "img",
4533
4663
  {
4534
- id: "avatar",
4535
- type: "url",
4536
- value: avatar,
4537
- onChange: (e) => setAvatar(e.target.value),
4538
- disabled: isLoading,
4664
+ src: user.avatar,
4665
+ alt: "Current avatar",
4539
4666
  style: {
4540
- width: "100%",
4541
- padding: "12px 16px",
4542
- border: `1px solid ${colors.borderSecondary}`,
4543
- borderRadius: "8px",
4544
- fontSize: "16px",
4545
- boxSizing: "border-box",
4546
- backgroundColor: colors.bgSecondary,
4547
- color: colors.textPrimary,
4548
- transition: "all 0.2s ease"
4549
- },
4550
- placeholder: "https://example.com/avatar.jpg"
4667
+ width: "80px",
4668
+ height: "80px",
4669
+ borderRadius: "50%",
4670
+ objectFit: "cover",
4671
+ border: `2px solid ${colors.borderSecondary}`
4672
+ }
4673
+ }
4674
+ ) }),
4675
+ /* @__PURE__ */ jsx(
4676
+ AvatarUploader,
4677
+ {
4678
+ upfilesConfig,
4679
+ onUploadComplete: handleAvatarUploadComplete,
4680
+ onError: handleAvatarUploadError,
4681
+ maxFileSize: 5 * 1024 * 1024,
4682
+ accept: ["image/*"]
4551
4683
  }
4552
4684
  )
4553
4685
  ] }),
@@ -4659,6 +4791,65 @@ var UserProfile = ({
4659
4791
  ] })
4660
4792
  ] });
4661
4793
  };
4794
+ var AvatarManager = ({
4795
+ open,
4796
+ onOpenChange,
4797
+ onAvatarUpdated,
4798
+ onError,
4799
+ title = "Select Avatar",
4800
+ description = "Choose an existing image or upload a new one",
4801
+ className,
4802
+ gridClassName,
4803
+ maxFileSize = 5 * 1024 * 1024,
4804
+ // 5MB default
4805
+ mode = "full",
4806
+ showDelete = false,
4807
+ upfilesConfig
4808
+ }) => {
4809
+ const { updateProfile } = useAuth2();
4810
+ const [updating, setUpdating] = useState(false);
4811
+ const handleSelect = async (image) => {
4812
+ setUpdating(true);
4813
+ try {
4814
+ const response = await updateProfile({ avatar: image.url });
4815
+ if (response.success && response.user?.avatar) {
4816
+ onAvatarUpdated?.(response.user.avatar);
4817
+ onOpenChange(false);
4818
+ } else {
4819
+ throw new Error(response.message || "Failed to update avatar");
4820
+ }
4821
+ } catch (error) {
4822
+ const err = error instanceof Error ? error : new Error("Failed to update avatar");
4823
+ onError?.(err);
4824
+ } finally {
4825
+ setUpdating(false);
4826
+ }
4827
+ };
4828
+ return /* @__PURE__ */ jsx(
4829
+ ImageManager,
4830
+ {
4831
+ open,
4832
+ onOpenChange,
4833
+ clientOptions: {
4834
+ baseUrl: upfilesConfig.baseUrl,
4835
+ apiKey: upfilesConfig.apiKey,
4836
+ apiKeyHeader: upfilesConfig.apiKeyHeader || "authorization",
4837
+ presignUrl: upfilesConfig.presignUrl,
4838
+ presignPath: upfilesConfig.presignPath
4839
+ },
4840
+ folderPath: upfilesConfig.folderPath || "avatars/",
4841
+ title,
4842
+ description,
4843
+ className,
4844
+ gridClassName,
4845
+ onSelect: handleSelect,
4846
+ maxFileSize,
4847
+ mode,
4848
+ showDelete,
4849
+ fetchThumbnails: true
4850
+ }
4851
+ );
4852
+ };
4662
4853
  var isServer = typeof window === "undefined";
4663
4854
  var useNextAuth = (config) => {
4664
4855
  const [authService] = useState(() => {
@@ -4851,6 +5042,6 @@ var useNextAuth = (config) => {
4851
5042
  };
4852
5043
  };
4853
5044
 
4854
- export { AuthFlow, AuthProvider, AuthService, AuthThemeProvider, ChangePassword, EmailVerificationPage, ForgotPassword, HttpClient, LoginForm, OtpForm, PhoneInput, ProtectedRoute, PublicRoute, RegisterForm, ResetPassword, SignIn, SignOut, SignUp, UserButton, UserProfile, VerifyEmail, useAuth2 as useAuth, useAuth as useAuthLegacy, useAuthTheme, useNextAuth };
5045
+ export { AuthFlow, AuthProvider, AuthService, AuthThemeProvider, AvatarManager, AvatarUploader, ChangePassword, EmailVerificationPage, ForgotPassword, HttpClient, LoginForm, OtpForm, PhoneInput, ProtectedRoute, PublicRoute, RegisterForm, ResetPassword, SignIn, SignOut, SignUp, UserButton, UserProfile, VerifyEmail, useAuth2 as useAuth, useAuth as useAuthLegacy, useAuthTheme, useNextAuth };
4855
5046
  //# sourceMappingURL=out.js.map
4856
5047
  //# sourceMappingURL=index.next.mjs.map