@blackcode_sa/metaestetics-api 1.14.69 → 1.14.73

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.14.69",
4
+ "version": "1.14.73",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -1,252 +1,225 @@
1
1
  /**
2
2
  * HTML email template for clinic welcome/confirmation email
3
3
  * Sent to clinic admins after successful signup
4
+ * Styled to match the appointment email templates
4
5
  */
5
6
  export const clinicWelcomeTemplate = `
6
7
  <!DOCTYPE html>
7
- <html>
8
+ <html lang="en">
8
9
  <head>
9
- <meta charset="UTF-8">
10
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
11
- <title>Welcome to MetaEsthetics</title>
12
- <style>
13
- body {
14
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
15
- line-height: 1.6;
16
- color: #333;
17
- margin: 0;
18
- padding: 0;
19
- background-color: #f5f5f5;
20
- }
21
- .container {
22
- max-width: 600px;
23
- margin: 0 auto;
24
- padding: 20px;
25
- }
26
- .header {
27
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
28
- padding: 40px 20px;
29
- text-align: center;
30
- border-radius: 8px 8px 0 0;
31
- }
32
- .header h1 {
33
- color: white;
34
- margin: 0;
35
- font-size: 28px;
36
- font-weight: 600;
37
- }
38
- .header p {
39
- color: rgba(255, 255, 255, 0.9);
40
- margin: 10px 0 0 0;
41
- font-size: 16px;
42
- }
43
- .content {
44
- padding: 40px 30px;
45
- background-color: #ffffff;
46
- }
47
- .greeting {
48
- font-size: 18px;
49
- color: #333;
50
- margin-bottom: 20px;
51
- }
52
- .message {
53
- font-size: 16px;
54
- color: #555;
55
- margin-bottom: 25px;
56
- }
57
- .highlight-box {
58
- background: linear-gradient(135deg, #f6f9fc 0%, #eef2f7 100%);
59
- border-left: 4px solid #667eea;
60
- padding: 20px;
61
- margin: 25px 0;
62
- border-radius: 0 8px 8px 0;
63
- }
64
- .highlight-box h3 {
65
- margin: 0 0 10px 0;
66
- color: #333;
67
- font-size: 16px;
68
- }
69
- .highlight-box p {
70
- margin: 0;
71
- color: #555;
72
- }
73
- .next-steps {
74
- margin: 30px 0;
75
- }
76
- .next-steps h3 {
77
- color: #333;
78
- font-size: 18px;
79
- margin-bottom: 15px;
80
- }
81
- .step {
82
- display: flex;
83
- align-items: flex-start;
84
- margin-bottom: 15px;
85
- }
86
- .step-number {
87
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
88
- color: white;
89
- width: 28px;
90
- height: 28px;
91
- border-radius: 50%;
92
- display: flex;
93
- align-items: center;
94
- justify-content: center;
95
- font-weight: 600;
96
- font-size: 14px;
97
- margin-right: 12px;
98
- flex-shrink: 0;
99
- }
100
- .step-content {
101
- flex: 1;
102
- padding-top: 3px;
103
- }
104
- .step-title {
105
- font-weight: 600;
106
- color: #333;
107
- margin-bottom: 3px;
108
- }
109
- .step-description {
110
- color: #666;
111
- font-size: 14px;
112
- }
113
- .button-container {
114
- text-align: center;
115
- margin: 35px 0;
116
- }
117
- .button {
118
- display: inline-block;
119
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
120
- color: white;
121
- text-decoration: none;
122
- padding: 14px 32px;
123
- border-radius: 6px;
124
- font-weight: 600;
125
- font-size: 16px;
126
- transition: transform 0.2s;
127
- }
128
- .button:hover {
129
- transform: translateY(-2px);
130
- }
131
- .support-box {
132
- background-color: #f8f9fa;
133
- padding: 20px;
134
- border-radius: 8px;
135
- margin-top: 30px;
136
- text-align: center;
137
- }
138
- .support-box p {
139
- margin: 0 0 10px 0;
140
- color: #555;
141
- }
142
- .support-box a {
143
- color: #667eea;
144
- text-decoration: none;
145
- font-weight: 500;
146
- }
147
- .footer {
148
- padding: 30px;
149
- text-align: center;
150
- background-color: #f8f9fa;
151
- border-radius: 0 0 8px 8px;
152
- }
153
- .footer p {
154
- margin: 5px 0;
155
- font-size: 13px;
156
- color: #888;
157
- }
158
- .footer a {
159
- color: #667eea;
160
- text-decoration: none;
161
- }
162
- .social-links {
163
- margin: 15px 0;
164
- }
165
- .social-links a {
166
- display: inline-block;
167
- margin: 0 8px;
168
- color: #888;
169
- text-decoration: none;
170
- }
171
- </style>
10
+ <meta charset="UTF-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
+ <title>Welcome to MetaEsthetics</title>
13
+ <!--[if mso]>
14
+ <noscript>
15
+ <xml>
16
+ <o:OfficeDocumentSettings>
17
+ <o:PixelsPerInch>96</o:PixelsPerInch>
18
+ </o:OfficeDocumentSettings>
19
+ </xml>
20
+ </noscript>
21
+ <![endif]-->
172
22
  </head>
173
- <body>
174
- <div class="container">
175
- <div class="header">
176
- <h1>Welcome to MetaEsthetics!</h1>
177
- <p>Your clinic registration is complete</p>
178
- </div>
179
- <div class="content">
180
- <p class="greeting">Hello {{adminName}},</p>
181
-
182
- <p class="message">
183
- Thank you for registering <strong>{{clinicGroupName}}</strong> with MetaEsthetics.
184
- Your account has been successfully created and you're now ready to start managing
185
- your aesthetic practice with our comprehensive platform.
186
- </p>
187
-
188
- <div class="highlight-box">
189
- <h3>Account Details</h3>
190
- <p><strong>Clinic Group:</strong> {{clinicGroupName}}</p>
191
- <p><strong>Admin Email:</strong> {{adminEmail}}</p>
192
- <p><strong>Registration Date:</strong> {{registrationDate}}</p>
193
- </div>
194
-
195
- <div class="next-steps">
196
- <h3>Get Started with MetaEsthetics</h3>
197
-
198
- <div class="step">
199
- <div class="step-number">1</div>
200
- <div class="step-content">
201
- <div class="step-title">Complete Your Profile</div>
202
- <div class="step-description">Add your clinic details, working hours, and upload photos to attract patients.</div>
203
- </div>
204
- </div>
205
-
206
- <div class="step">
207
- <div class="step-number">2</div>
208
- <div class="step-content">
209
- <div class="step-title">Add Your Team</div>
210
- <div class="step-description">Invite practitioners and staff members to join your clinic.</div>
211
- </div>
212
- </div>
213
-
214
- <div class="step">
215
- <div class="step-number">3</div>
216
- <div class="step-content">
217
- <div class="step-title">Set Up Services</div>
218
- <div class="step-description">Configure your procedures, pricing, and availability for bookings.</div>
219
- </div>
220
- </div>
221
-
222
- <div class="step">
223
- <div class="step-number">4</div>
224
- <div class="step-content">
225
- <div class="step-title">Start Accepting Patients</div>
226
- <div class="step-description">Begin managing appointments and growing your practice.</div>
227
- </div>
228
- </div>
229
- </div>
230
-
231
- <div class="button-container">
232
- <a href="{{dashboardUrl}}" class="button">Go to Dashboard</a>
233
- </div>
234
-
235
- <div class="support-box">
236
- <p>Need help getting started?</p>
237
- <p>Contact our support team at <a href="mailto:{{supportEmail}}">{{supportEmail}}</a></p>
238
- </div>
239
- </div>
240
- <div class="footer">
241
- <p>This is an automated message from MetaEsthetics.</p>
242
- <p>&copy; {{currentYear}} MetaEsthetics. All rights reserved.</p>
243
- <div class="social-links">
244
- <a href="https://metaesthetics.net">Website</a> |
245
- <a href="https://metaesthetics.net/privacy">Privacy Policy</a> |
246
- <a href="https://metaesthetics.net/terms">Terms of Service</a>
247
- </div>
248
- </div>
249
- </div>
23
+ <body style="margin: 0; padding: 0; background-color: #f8f6f5; font-family: Georgia, 'Times New Roman', serif;">
24
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background-color: #f8f6f5;">
25
+ <tr>
26
+ <td align="center" style="padding: 40px 20px;">
27
+ <table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width: 600px; background-color: #ffffff; border-radius: 2px; box-shadow: 0 1px 3px rgba(103, 87, 74, 0.08);">
28
+
29
+ <!-- Header -->
30
+ <tr>
31
+ <td style="padding: 48px 48px 32px 48px; border-bottom: 1px solid #f0ebe6;">
32
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
33
+ <tr>
34
+ <td>
35
+ <h1 style="margin: 0; font-size: 24px; font-weight: 400; color: #67574A; letter-spacing: 2px; text-transform: uppercase; font-family: Georgia, 'Times New Roman', serif;">MetaEsthetics</h1>
36
+ </td>
37
+ </tr>
38
+ </table>
39
+ </td>
40
+ </tr>
41
+
42
+ <!-- Welcome Banner -->
43
+ <tr>
44
+ <td style="padding: 0;">
45
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background-color: #00BB38;">
46
+ <tr>
47
+ <td style="padding: 20px 48px;">
48
+ <p style="margin: 0; font-size: 13px; font-weight: 600; color: #ffffff; letter-spacing: 1.5px; text-transform: uppercase; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Welcome to MetaEsthetics</p>
49
+ </td>
50
+ </tr>
51
+ </table>
52
+ </td>
53
+ </tr>
54
+
55
+ <!-- Main Content -->
56
+ <tr>
57
+ <td style="padding: 40px 48px;">
58
+
59
+ <!-- Greeting -->
60
+ <p style="margin: 0 0 24px 0; font-size: 17px; color: #333333; line-height: 1.6; font-family: Georgia, 'Times New Roman', serif;">
61
+ Dear {{adminName}},
62
+ </p>
63
+
64
+ <p style="margin: 0 0 32px 0; font-size: 16px; color: #555555; line-height: 1.7; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">
65
+ Thank you for registering <strong style="color: #67574A;">{{clinicGroupName}}</strong> with MetaEsthetics. Your account has been successfully created and you're now ready to start managing your aesthetic practice.
66
+ </p>
67
+
68
+ <!-- Account Details Card -->
69
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 32px; background-color: #faf9f7; border-left: 3px solid #00BB38;">
70
+ <tr>
71
+ <td style="padding: 24px;">
72
+ <p style="margin: 0 0 16px 0; font-size: 12px; font-weight: 600; color: #868686; letter-spacing: 1px; text-transform: uppercase; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Account Details</p>
73
+
74
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
75
+ <tr>
76
+ <td style="padding-bottom: 12px;">
77
+ <p style="margin: 0 0 2px 0; font-size: 11px; color: #868686; text-transform: uppercase; letter-spacing: 0.5px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Clinic Group</p>
78
+ <p style="margin: 0; font-size: 15px; color: #67574A; font-weight: 500; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">{{clinicGroupName}}</p>
79
+ </td>
80
+ </tr>
81
+ <tr>
82
+ <td style="padding-bottom: 12px;">
83
+ <p style="margin: 0 0 2px 0; font-size: 11px; color: #868686; text-transform: uppercase; letter-spacing: 0.5px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Admin Email</p>
84
+ <p style="margin: 0; font-size: 15px; color: #333333; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">{{adminEmail}}</p>
85
+ </td>
86
+ </tr>
87
+ <tr>
88
+ <td>
89
+ <p style="margin: 0 0 2px 0; font-size: 11px; color: #868686; text-transform: uppercase; letter-spacing: 0.5px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Registration Date</p>
90
+ <p style="margin: 0; font-size: 15px; color: #333333; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">{{registrationDate}}</p>
91
+ </td>
92
+ </tr>
93
+ </table>
94
+ </td>
95
+ </tr>
96
+ </table>
97
+
98
+ <!-- Divider -->
99
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 32px;">
100
+ <tr>
101
+ <td style="height: 1px; background-color: #e8e4df;"></td>
102
+ </tr>
103
+ </table>
104
+
105
+ <!-- Getting Started Section -->
106
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 32px;">
107
+ <tr>
108
+ <td>
109
+ <p style="margin: 0 0 20px 0; font-size: 14px; font-weight: 600; color: #67574A; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Getting Started</p>
110
+ </td>
111
+ </tr>
112
+ </table>
113
+
114
+ <!-- Step 1 -->
115
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 16px;">
116
+ <tr>
117
+ <td style="padding: 16px 20px; background-color: #faf9f7; border-left: 3px solid #a48a76;">
118
+ <p style="margin: 0 0 4px 0; font-size: 11px; font-weight: 600; color: #a48a76; letter-spacing: 1px; text-transform: uppercase; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Step 1</p>
119
+ <p style="margin: 0 0 4px 0; font-size: 15px; color: #333333; font-weight: 500; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Complete Your Profile</p>
120
+ <p style="margin: 0; font-size: 14px; color: #555555; line-height: 1.5; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Add your clinic details, working hours, and upload photos to attract patients.</p>
121
+ </td>
122
+ </tr>
123
+ </table>
124
+
125
+ <!-- Step 2 -->
126
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 16px;">
127
+ <tr>
128
+ <td style="padding: 16px 20px; background-color: #faf9f7; border-left: 3px solid #a48a76;">
129
+ <p style="margin: 0 0 4px 0; font-size: 11px; font-weight: 600; color: #a48a76; letter-spacing: 1px; text-transform: uppercase; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Step 2</p>
130
+ <p style="margin: 0 0 4px 0; font-size: 15px; color: #333333; font-weight: 500; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Add Your Team</p>
131
+ <p style="margin: 0; font-size: 14px; color: #555555; line-height: 1.5; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Invite practitioners and staff members to join your clinic.</p>
132
+ </td>
133
+ </tr>
134
+ </table>
135
+
136
+ <!-- Step 3 -->
137
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 16px;">
138
+ <tr>
139
+ <td style="padding: 16px 20px; background-color: #faf9f7; border-left: 3px solid #a48a76;">
140
+ <p style="margin: 0 0 4px 0; font-size: 11px; font-weight: 600; color: #a48a76; letter-spacing: 1px; text-transform: uppercase; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Step 3</p>
141
+ <p style="margin: 0 0 4px 0; font-size: 15px; color: #333333; font-weight: 500; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Set Up Services</p>
142
+ <p style="margin: 0; font-size: 14px; color: #555555; line-height: 1.5; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Configure your procedures, pricing, and availability for bookings.</p>
143
+ </td>
144
+ </tr>
145
+ </table>
146
+
147
+ <!-- Step 4 -->
148
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 32px;">
149
+ <tr>
150
+ <td style="padding: 16px 20px; background-color: #f0faf4; border-left: 3px solid #00BB38;">
151
+ <p style="margin: 0 0 4px 0; font-size: 11px; font-weight: 600; color: #00BB38; letter-spacing: 1px; text-transform: uppercase; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Step 4</p>
152
+ <p style="margin: 0 0 4px 0; font-size: 15px; color: #333333; font-weight: 500; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Start Accepting Patients</p>
153
+ <p style="margin: 0; font-size: 14px; color: #555555; line-height: 1.5; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Begin managing appointments and growing your practice.</p>
154
+ </td>
155
+ </tr>
156
+ </table>
157
+
158
+ <!-- CTA Button -->
159
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 32px;">
160
+ <tr>
161
+ <td align="center">
162
+ <a href="{{dashboardUrl}}" style="display: inline-block; background-color: #67574A; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 4px; font-size: 14px; font-weight: 600; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; letter-spacing: 0.5px;">Go to Dashboard</a>
163
+ </td>
164
+ </tr>
165
+ </table>
166
+
167
+ <!-- Divider -->
168
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 32px;">
169
+ <tr>
170
+ <td style="height: 1px; background-color: #e8e4df;"></td>
171
+ </tr>
172
+ </table>
173
+
174
+ <!-- Support Info -->
175
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin-bottom: 24px;">
176
+ <tr>
177
+ <td style="padding: 20px 24px; background-color: #faf9f7; border-left: 3px solid #a48a76;">
178
+ <p style="margin: 0 0 8px 0; font-size: 13px; font-weight: 600; color: #67574A; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Need Help?</p>
179
+ <p style="margin: 0; font-size: 14px; color: #555555; line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">
180
+ Our support team is here to help you get started. Contact us at <a href="mailto:{{supportEmail}}" style="color: #a48a76; text-decoration: none;">{{supportEmail}}</a>
181
+ </p>
182
+ </td>
183
+ </tr>
184
+ </table>
185
+
186
+ <!-- Thank You -->
187
+ <p style="margin: 0; font-size: 14px; color: #868686; line-height: 1.7; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">
188
+ Thank you for choosing MetaEsthetics. We look forward to supporting your practice.
189
+ </p>
190
+
191
+ </td>
192
+ </tr>
193
+
194
+ <!-- Footer -->
195
+ <tr>
196
+ <td style="padding: 32px 48px; background-color: #faf9f7; border-top: 1px solid #f0ebe6;">
197
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
198
+ <tr>
199
+ <td>
200
+ <p style="margin: 0 0 8px 0; font-size: 14px; font-weight: 500; color: #67574A; font-family: Georgia, 'Times New Roman', serif;">MetaEsthetics</p>
201
+ <p style="margin: 0 0 16px 0; font-size: 13px; color: #868686; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">Premium Aesthetic Services Platform</p>
202
+ <p style="margin: 0; font-size: 11px; color: #aaaaaa; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">This is an automated message from MetaEsthetics. Please do not reply directly to this email.</p>
203
+ </td>
204
+ </tr>
205
+ </table>
206
+ </td>
207
+ </tr>
208
+
209
+ </table>
210
+
211
+ <!-- Copyright -->
212
+ <table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width: 600px;">
213
+ <tr>
214
+ <td style="padding: 24px 48px; text-align: center;">
215
+ <p style="margin: 0; font-size: 11px; color: #aaaaaa; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">&copy; {{currentYear}} MetaEsthetics. All rights reserved.</p>
216
+ </td>
217
+ </tr>
218
+ </table>
219
+
220
+ </td>
221
+ </tr>
222
+ </table>
250
223
  </body>
251
224
  </html>
252
225
  `;
@@ -17,6 +17,7 @@ import {
17
17
  confirmPasswordReset,
18
18
  fetchSignInMethodsForEmail,
19
19
  signInWithCredential,
20
+ OAuthProvider,
20
21
  } from 'firebase/auth';
21
22
  import {
22
23
  getFirestore,
@@ -1411,4 +1412,82 @@ export class AuthService extends BaseService {
1411
1412
  throw handleFirebaseError(error);
1412
1413
  }
1413
1414
  }
1415
+
1416
+ /**
1417
+ * Sign in with Apple ID token (for existing users only).
1418
+ * Mirrors signInWithGoogleIdToken — verifies the user exists before allowing sign-in.
1419
+ */
1420
+ async signInWithAppleIdToken(
1421
+ idToken: string,
1422
+ rawNonce: string,
1423
+ appleUserInfo?: { fullName?: { givenName?: string; familyName?: string }; email?: string },
1424
+ ): Promise<User> {
1425
+ try {
1426
+ console.log('[AUTH] Signing in with Apple ID Token');
1427
+
1428
+ // Build Apple OAuth credential
1429
+ const provider = new OAuthProvider('apple.com');
1430
+ const credential = provider.credential({ idToken, rawNonce });
1431
+
1432
+ // Sign in to Firebase
1433
+ const { user: firebaseUser } = await signInWithCredential(this.auth, credential);
1434
+ console.log('[AUTH] Firebase user signed in via Apple:', firebaseUser.uid);
1435
+
1436
+ // Load domain user document
1437
+ const existingUser = await this.userService.getUserById(firebaseUser.uid);
1438
+ if (existingUser) {
1439
+ console.log('[AUTH] Existing user found, returning profile:', existingUser.uid);
1440
+ return existingUser;
1441
+ }
1442
+
1443
+ // No user document — sign out and error
1444
+ console.log('[AUTH] No existing MetaEstetics user for Apple account – signing out.');
1445
+ await firebaseSignOut(this.auth);
1446
+ throw new AuthError(
1447
+ 'No account found. Please complete registration by starting with "Get Started".',
1448
+ 'AUTH/USER_NOT_FOUND',
1449
+ 404,
1450
+ );
1451
+ } catch (error) {
1452
+ console.error('[AUTH] Error in signInWithAppleIdToken:', error);
1453
+ throw handleFirebaseError(error);
1454
+ }
1455
+ }
1456
+
1457
+ /**
1458
+ * Link an Apple account to the current user (anonymous → full account upgrade).
1459
+ * Mirrors linkGoogleAccount.
1460
+ */
1461
+ async linkAppleAccount(idToken: string, rawNonce: string): Promise<User> {
1462
+ try {
1463
+ console.log('[AUTH] Linking Apple account with ID Token');
1464
+ const currentUser = this.auth.currentUser;
1465
+ if (!currentUser) {
1466
+ throw AUTH_ERRORS.NOT_AUTHENTICATED;
1467
+ }
1468
+
1469
+ const wasAnonymous = currentUser.isAnonymous;
1470
+ console.log(`[AUTH] Current user is ${wasAnonymous ? 'anonymous' : 'not anonymous'}`);
1471
+
1472
+ const provider = new OAuthProvider('apple.com');
1473
+ const credential = provider.credential({ idToken, rawNonce });
1474
+ const userCredential = await linkWithCredential(currentUser, credential);
1475
+ const linkedFirebaseUser = userCredential.user;
1476
+ console.log('[AUTH] Apple account linked successfully to user:', linkedFirebaseUser.uid);
1477
+
1478
+ if (wasAnonymous) {
1479
+ console.log('[AUTH] Upgrading anonymous user profile');
1480
+ const email = linkedFirebaseUser.email || '';
1481
+ return await this.userService.upgradeAnonymousUser(
1482
+ linkedFirebaseUser.uid,
1483
+ email,
1484
+ );
1485
+ }
1486
+
1487
+ return (await this.userService.getUserById(linkedFirebaseUser.uid))!;
1488
+ } catch (error) {
1489
+ console.error('[AUTH] Error in linkAppleAccount:', error);
1490
+ throw handleFirebaseError(error);
1491
+ }
1492
+ }
1414
1493
  }