medusa-storefront-data 1.0.0
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/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +31 -0
- package/dist/cookies.d.ts +23 -0
- package/dist/cookies.d.ts.map +1 -0
- package/dist/cookies.js +140 -0
- package/dist/server/cart.d.ts +92 -0
- package/dist/server/cart.d.ts.map +1 -0
- package/dist/server/cart.js +827 -0
- package/dist/server/categories.d.ts +3 -0
- package/dist/server/categories.d.ts.map +1 -0
- package/dist/server/categories.js +71 -0
- package/dist/server/collections.d.ts +8 -0
- package/dist/server/collections.d.ts.map +1 -0
- package/dist/server/collections.js +84 -0
- package/dist/server/customer-registration.d.ts +142 -0
- package/dist/server/customer-registration.d.ts.map +1 -0
- package/dist/server/customer-registration.js +295 -0
- package/dist/server/customer.d.ts +48 -0
- package/dist/server/customer.d.ts.map +1 -0
- package/dist/server/customer.js +462 -0
- package/dist/server/dynamic-config.d.ts +125 -0
- package/dist/server/dynamic-config.d.ts.map +1 -0
- package/dist/server/dynamic-config.js +263 -0
- package/dist/server/fulfillment.d.ts +4 -0
- package/dist/server/fulfillment.d.ts.map +1 -0
- package/dist/server/fulfillment.js +72 -0
- package/dist/server/guest.d.ts +109 -0
- package/dist/server/guest.d.ts.map +1 -0
- package/dist/server/guest.js +304 -0
- package/dist/server/index.d.ts +21 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +20 -0
- package/dist/server/locale-actions.d.ts +14 -0
- package/dist/server/locale-actions.d.ts.map +1 -0
- package/dist/server/locale-actions.js +63 -0
- package/dist/server/locales.d.ts +10 -0
- package/dist/server/locales.d.ts.map +1 -0
- package/dist/server/locales.js +20 -0
- package/dist/server/notifications.d.ts +2 -0
- package/dist/server/notifications.d.ts.map +1 -0
- package/dist/server/notifications.js +20 -0
- package/dist/server/onboarding.d.ts +2 -0
- package/dist/server/onboarding.d.ts.map +1 -0
- package/dist/server/onboarding.js +8 -0
- package/dist/server/orders.d.ts +69 -0
- package/dist/server/orders.d.ts.map +1 -0
- package/dist/server/orders.js +371 -0
- package/dist/server/payment-details.d.ts +5 -0
- package/dist/server/payment-details.d.ts.map +1 -0
- package/dist/server/payment-details.js +53 -0
- package/dist/server/payment.d.ts +2 -0
- package/dist/server/payment.d.ts.map +1 -0
- package/dist/server/payment.js +25 -0
- package/dist/server/products.d.ts +58 -0
- package/dist/server/products.d.ts.map +1 -0
- package/dist/server/products.js +285 -0
- package/dist/server/regions.d.ts +5 -0
- package/dist/server/regions.d.ts.map +1 -0
- package/dist/server/regions.js +54 -0
- package/dist/server/returns.d.ts +29 -0
- package/dist/server/returns.d.ts.map +1 -0
- package/dist/server/returns.js +236 -0
- package/dist/server/swaps.d.ts +14 -0
- package/dist/server/swaps.d.ts.map +1 -0
- package/dist/server/swaps.js +123 -0
- package/dist/server/variants.d.ts +3 -0
- package/dist/server/variants.d.ts.map +1 -0
- package/dist/server/variants.js +26 -0
- package/dist/util/get-locale-header.d.ts +4 -0
- package/dist/util/get-locale-header.d.ts.map +1 -0
- package/dist/util/get-locale-header.js +7 -0
- package/dist/util/medusa-error.d.ts +2 -0
- package/dist/util/medusa-error.d.ts.map +1 -0
- package/dist/util/medusa-error.js +18 -0
- package/package.json +152 -0
- package/src/config.ts +39 -0
- package/src/cookies.ts +171 -0
- package/src/middleware.ts +2 -0
- package/src/server/cart.ts +1054 -0
- package/src/server/categories.ts +94 -0
- package/src/server/collections.ts +113 -0
- package/src/server/customer-registration.ts +349 -0
- package/src/server/customer.ts +581 -0
- package/src/server/dynamic-config.ts +403 -0
- package/src/server/fulfillment.ts +97 -0
- package/src/server/guest.ts +333 -0
- package/src/server/index.ts +21 -0
- package/src/server/locale-actions.ts +74 -0
- package/src/server/locales.ts +28 -0
- package/src/server/notifications.ts +22 -0
- package/src/server/onboarding.ts +9 -0
- package/src/server/orders.ts +467 -0
- package/src/server/payment-details.ts +69 -0
- package/src/server/payment.ts +35 -0
- package/src/server/products.ts +378 -0
- package/src/server/regions.ts +66 -0
- package/src/server/returns.ts +294 -0
- package/src/server/swaps.ts +150 -0
- package/src/server/variants.ts +38 -0
- package/src/server/wishlist.ts +64 -0
- package/src/services/middleware.ts +54 -0
- package/src/util/get-locale-header.ts +8 -0
- package/src/util/medusa-error.ts +19 -0
- package/src/util/sort-products.ts +47 -0
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
"use server"
|
|
2
|
+
|
|
3
|
+
import { sdk } from "../config"
|
|
4
|
+
import medusaError from "../util/medusa-error"
|
|
5
|
+
import { HttpTypes } from "@medusajs/types"
|
|
6
|
+
import { revalidateTag } from "next/cache"
|
|
7
|
+
import { redirect } from "next/navigation"
|
|
8
|
+
import { cookies } from "next/headers"
|
|
9
|
+
import {
|
|
10
|
+
getAuthHeaders,
|
|
11
|
+
getCacheOptions,
|
|
12
|
+
getCacheTag,
|
|
13
|
+
getCartId,
|
|
14
|
+
removeAuthToken,
|
|
15
|
+
removeCartId,
|
|
16
|
+
removeSyncLock,
|
|
17
|
+
setAuthToken,
|
|
18
|
+
setCartId,
|
|
19
|
+
} from "../cookies"
|
|
20
|
+
|
|
21
|
+
import { cache } from "react"
|
|
22
|
+
|
|
23
|
+
export const retrieveCustomer = cache(
|
|
24
|
+
async (): Promise<HttpTypes.StoreCustomer | null> => {
|
|
25
|
+
const authHeaders = await getAuthHeaders()
|
|
26
|
+
|
|
27
|
+
if (!authHeaders) return null
|
|
28
|
+
|
|
29
|
+
const headers = {
|
|
30
|
+
...authHeaders,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const { customer } = await sdk.client.fetch<{ customer: HttpTypes.StoreCustomer }>(
|
|
35
|
+
`/store/customers/me`,
|
|
36
|
+
{
|
|
37
|
+
method: "GET",
|
|
38
|
+
query: { fields: "*addresses" },
|
|
39
|
+
headers: headers as Record<string, string>,
|
|
40
|
+
cache: "no-store",
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
return customer
|
|
44
|
+
} catch (error: any) {
|
|
45
|
+
const errStr = error.toString().toLowerCase()
|
|
46
|
+
// If the error indicates a pending deletion, return a special object
|
|
47
|
+
if (errStr.includes("active account deletion request") ||
|
|
48
|
+
(error.message && error.message.toLowerCase().includes("active account deletion request"))) {
|
|
49
|
+
const cookieStore = await cookies()
|
|
50
|
+
const cookieEmail = cookieStore.get("_medusa_customer_email")?.value
|
|
51
|
+
return { id: "pending_deletion", email: cookieEmail || "pending" } as any
|
|
52
|
+
}
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
export const updateCustomer = async (body: HttpTypes.StoreUpdateCustomer) => {
|
|
59
|
+
const headers = {
|
|
60
|
+
...(await getAuthHeaders()),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const updateRes = await sdk.store.customer
|
|
64
|
+
.update(body, {}, headers)
|
|
65
|
+
.then(({ customer }) => customer)
|
|
66
|
+
.catch(medusaError)
|
|
67
|
+
|
|
68
|
+
const cacheTag = await getCacheTag("customers")
|
|
69
|
+
revalidateTag(cacheTag)
|
|
70
|
+
|
|
71
|
+
return updateRes
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function signup(_currentState: unknown, formData: FormData) {
|
|
75
|
+
const password = formData.get("password") as string
|
|
76
|
+
const customerForm = {
|
|
77
|
+
email: formData.get("email") as string,
|
|
78
|
+
first_name: formData.get("first_name") as string,
|
|
79
|
+
last_name: formData.get("last_name") as string,
|
|
80
|
+
phone: (formData.get("phone") as string)?.replace(/[^\d+]/g, ""),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const token = await sdk.auth.register("customer", "emailpass", {
|
|
85
|
+
email: customerForm.email,
|
|
86
|
+
password: password,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
await setAuthToken(token as string)
|
|
90
|
+
|
|
91
|
+
const headers = {
|
|
92
|
+
...(await getAuthHeaders()),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const { customer: createdCustomer } = await sdk.store.customer.create(
|
|
96
|
+
customerForm,
|
|
97
|
+
{},
|
|
98
|
+
headers
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
const loginToken = await sdk.auth.login("customer", "emailpass", {
|
|
102
|
+
email: customerForm.email,
|
|
103
|
+
password,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
await setAuthToken(loginToken as string)
|
|
107
|
+
|
|
108
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
109
|
+
revalidateTag(customerCacheTag)
|
|
110
|
+
|
|
111
|
+
await transferCart(loginToken as string)
|
|
112
|
+
|
|
113
|
+
return createdCustomer
|
|
114
|
+
} catch (error: any) {
|
|
115
|
+
return error.toString()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function login(_currentState: unknown, formData: FormData) {
|
|
120
|
+
let email = formData.get("email_or_phone") as string
|
|
121
|
+
const password = formData.get("password") as string
|
|
122
|
+
|
|
123
|
+
// Sanitize phone number: remove spaces, dashes, etc if it looks like a phone number
|
|
124
|
+
// If it doesn't have '@', we treat it as a potential phone number and clean it
|
|
125
|
+
if (email && !email.includes("@")) {
|
|
126
|
+
email = email.replace(/[^\d+]/g, "")
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let shouldRedirect = false
|
|
130
|
+
let countryCode = "in"
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const token = await sdk.auth.login("customer", "emailpass", { email_or_phone: email, password })
|
|
134
|
+
await setAuthToken(token as string)
|
|
135
|
+
|
|
136
|
+
// Store email in cookie for deletion flow (needed for cancellation modal)
|
|
137
|
+
const cookieStore = await cookies()
|
|
138
|
+
cookieStore.set("_medusa_customer_email", email, { maxAge: 60 * 60 * 24 * 7, path: "/" })
|
|
139
|
+
|
|
140
|
+
// Force a check to see if we are blocked
|
|
141
|
+
try {
|
|
142
|
+
await sdk.store.customer.retrieve({}, {
|
|
143
|
+
authorization: `Bearer ${token}`
|
|
144
|
+
})
|
|
145
|
+
} catch (err: any) {
|
|
146
|
+
const errStr = err.toString().toLowerCase()
|
|
147
|
+
if (errStr.includes("active account deletion request")) {
|
|
148
|
+
return "ACCOUNT_DELETION_PENDING"
|
|
149
|
+
}
|
|
150
|
+
// If it's another error, we might still want to know, but deletion is our priority
|
|
151
|
+
throw err
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Transfer cart after login success - Moved here to ensure execution
|
|
155
|
+
try {
|
|
156
|
+
await transferCart(token as string)
|
|
157
|
+
} catch (e) {
|
|
158
|
+
console.error("Transfer cart failed in login:", e)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
162
|
+
revalidateTag(customerCacheTag)
|
|
163
|
+
|
|
164
|
+
countryCode = formData.get("country_code") as string || "in"
|
|
165
|
+
shouldRedirect = true
|
|
166
|
+
} catch (error: any) {
|
|
167
|
+
const errorStr = error.toString().toLowerCase()
|
|
168
|
+
|
|
169
|
+
if (errorStr.includes("active account deletion request") ||
|
|
170
|
+
(error.message && error.message.toLowerCase().includes("active account deletion request"))) {
|
|
171
|
+
// Still set email cookie even if blocked
|
|
172
|
+
const cookieStore = await cookies()
|
|
173
|
+
cookieStore.set("_medusa_customer_email", email, { maxAge: 60 * 60 * 24 * 7, path: "/" })
|
|
174
|
+
return "ACCOUNT_DELETION_PENDING"
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (errorStr.includes("invalid") || errorStr.includes("401") || errorStr.includes("not found")) {
|
|
178
|
+
return "Invalid credentials. Please try again."
|
|
179
|
+
}
|
|
180
|
+
return error.message || error.toString()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (shouldRedirect) {
|
|
184
|
+
const redirectUrl = formData.get("redirect_url") as string
|
|
185
|
+
|
|
186
|
+
// Check if it's a "keep-me-here" context (Wishlist or Review Popups)
|
|
187
|
+
const hasKeepContext = redirectUrl?.includes("login_context=keep")
|
|
188
|
+
|
|
189
|
+
// Security + Context check
|
|
190
|
+
if (redirectUrl && redirectUrl.startsWith("/") && hasKeepContext) {
|
|
191
|
+
redirect(redirectUrl)
|
|
192
|
+
} else {
|
|
193
|
+
// DEFAULT: Always Home Page
|
|
194
|
+
redirect(`/${countryCode}`)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export async function signout(countryCode: string) {
|
|
200
|
+
await sdk.auth.logout()
|
|
201
|
+
|
|
202
|
+
await removeAuthToken()
|
|
203
|
+
await removeCartId()
|
|
204
|
+
await removeSyncLock()
|
|
205
|
+
|
|
206
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
207
|
+
revalidateTag(customerCacheTag)
|
|
208
|
+
|
|
209
|
+
await removeCartId()
|
|
210
|
+
|
|
211
|
+
const cartCacheTag = await getCacheTag("carts")
|
|
212
|
+
revalidateTag(cartCacheTag)
|
|
213
|
+
|
|
214
|
+
redirect(`/${countryCode}`)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export async function transferCart(token?: string) {
|
|
218
|
+
const cartId = await getCartId()
|
|
219
|
+
const authHeaders = token
|
|
220
|
+
? { authorization: `Bearer ${token}` }
|
|
221
|
+
: await getAuthHeaders()
|
|
222
|
+
|
|
223
|
+
console.log("--- TransferCart Debug ---")
|
|
224
|
+
console.log("Token provided:", !!token)
|
|
225
|
+
console.log("Guest Cart ID:", cartId)
|
|
226
|
+
|
|
227
|
+
const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY
|
|
228
|
+
const backendUrl = process.env.MEDUSA_BACKEND_URL || process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL || "http://localhost:9000"
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
console.log("Calling merge API (always)...")
|
|
232
|
+
|
|
233
|
+
const response = await fetch(`${backendUrl}/store/carts/merge`, {
|
|
234
|
+
method: "POST",
|
|
235
|
+
headers: {
|
|
236
|
+
"Content-Type": "application/json",
|
|
237
|
+
"x-publishable-api-key": publishableKey || "",
|
|
238
|
+
...(authHeaders as any),
|
|
239
|
+
},
|
|
240
|
+
// Only include body if cartId exists
|
|
241
|
+
...(cartId ? { body: JSON.stringify({ cart_id: cartId }) } : {}),
|
|
242
|
+
cache: "no-store",
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
if (response.ok) {
|
|
246
|
+
const data = await response.json()
|
|
247
|
+
console.log("--- Login Merge Response (JSON) ---")
|
|
248
|
+
console.log(JSON.stringify(data, null, 2))
|
|
249
|
+
console.log("-----------------------------------")
|
|
250
|
+
|
|
251
|
+
const activeCartId = data.cart?.id || data.id || data.cart_id
|
|
252
|
+
|
|
253
|
+
if (activeCartId) {
|
|
254
|
+
console.log("Storing Cart ID in cookie:", activeCartId)
|
|
255
|
+
await setCartId(activeCartId)
|
|
256
|
+
}
|
|
257
|
+
} else if (response.status === 404) {
|
|
258
|
+
// 404 is fine - it just means the customer has no active cart yet
|
|
259
|
+
console.log("No active cart found for this customer (expected).")
|
|
260
|
+
} else {
|
|
261
|
+
const errorText = await response.text()
|
|
262
|
+
console.log(`Merge API Info (${response.status}):`, errorText)
|
|
263
|
+
if (cartId) {
|
|
264
|
+
await sdk.store.cart.transferCart(cartId, {}, authHeaders).catch(() => { })
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} catch (error: any) {
|
|
268
|
+
console.error("Merge API error:", error.message)
|
|
269
|
+
if (cartId) {
|
|
270
|
+
await sdk.store.cart.transferCart(cartId, {}, authHeaders).catch(() => { })
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
console.log("--------------------------")
|
|
275
|
+
const cartCacheTag = await getCacheTag("carts")
|
|
276
|
+
revalidateTag(cartCacheTag)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export const addCustomerAddress = async (
|
|
280
|
+
currentState: Record<string, unknown>,
|
|
281
|
+
formData: FormData
|
|
282
|
+
): Promise<any> => {
|
|
283
|
+
const isDefaultBilling = (currentState.isDefaultBilling as boolean) || false
|
|
284
|
+
const isDefaultShipping = (currentState.isDefaultShipping as boolean) || false
|
|
285
|
+
|
|
286
|
+
const address: any = {
|
|
287
|
+
first_name: formData.get("first_name") as string,
|
|
288
|
+
last_name: formData.get("last_name") as string,
|
|
289
|
+
company: formData.get("company") as string,
|
|
290
|
+
address_1: formData.get("address_1") as string,
|
|
291
|
+
address_2: formData.get("address_2") as string,
|
|
292
|
+
city: formData.get("city") as string,
|
|
293
|
+
postal_code: formData.get("postal_code") as string,
|
|
294
|
+
province: formData.get("province") as string,
|
|
295
|
+
country_code: formData.get("country_code") as string,
|
|
296
|
+
phone: formData.get("phone") as string,
|
|
297
|
+
is_default_billing: isDefaultBilling,
|
|
298
|
+
is_default_shipping: isDefaultShipping,
|
|
299
|
+
metadata: {
|
|
300
|
+
address_type: (formData.get("address_type") as string) || "HOME",
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const headers = {
|
|
305
|
+
...(await getAuthHeaders()),
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return sdk.store.customer
|
|
309
|
+
.createAddress(address, {}, headers)
|
|
310
|
+
.then(async ({ customer }) => {
|
|
311
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
312
|
+
revalidateTag(customerCacheTag)
|
|
313
|
+
return { success: true, error: null }
|
|
314
|
+
})
|
|
315
|
+
.catch((err) => {
|
|
316
|
+
return { success: false, error: err.toString() }
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export const deleteCustomerAddress = async (
|
|
321
|
+
addressId: string
|
|
322
|
+
): Promise<void> => {
|
|
323
|
+
const headers = {
|
|
324
|
+
...(await getAuthHeaders()),
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
await sdk.store.customer
|
|
328
|
+
.deleteAddress(addressId, headers)
|
|
329
|
+
.then(async () => {
|
|
330
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
331
|
+
revalidateTag(customerCacheTag)
|
|
332
|
+
return { success: true, error: null }
|
|
333
|
+
})
|
|
334
|
+
.catch((err) => {
|
|
335
|
+
return { success: false, error: err.toString() }
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export const updateCustomerAddress = async (
|
|
340
|
+
currentState: Record<string, unknown>,
|
|
341
|
+
formData: FormData
|
|
342
|
+
): Promise<any> => {
|
|
343
|
+
const addressId =
|
|
344
|
+
(currentState.addressId as string) || (formData.get("addressId") as string)
|
|
345
|
+
|
|
346
|
+
if (!addressId) {
|
|
347
|
+
return { success: false, error: "Address ID is required" }
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const address = {
|
|
351
|
+
first_name: formData.get("first_name") as string,
|
|
352
|
+
last_name: formData.get("last_name") as string,
|
|
353
|
+
company: formData.get("company") as string,
|
|
354
|
+
address_1: formData.get("address_1") as string,
|
|
355
|
+
address_2: formData.get("address_2") as string,
|
|
356
|
+
city: formData.get("city") as string,
|
|
357
|
+
postal_code: formData.get("postal_code") as string,
|
|
358
|
+
province: formData.get("province") as string,
|
|
359
|
+
country_code: formData.get("country_code") as string,
|
|
360
|
+
metadata: {
|
|
361
|
+
address_type: (formData.get("address_type") as string) || "HOME",
|
|
362
|
+
}
|
|
363
|
+
} as any
|
|
364
|
+
|
|
365
|
+
const phone = formData.get("phone") as string
|
|
366
|
+
|
|
367
|
+
if (phone) {
|
|
368
|
+
address.phone = phone
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const headers = {
|
|
372
|
+
...(await getAuthHeaders()),
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return sdk.store.customer
|
|
376
|
+
.updateAddress(addressId, address, {}, headers)
|
|
377
|
+
.then(async () => {
|
|
378
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
379
|
+
revalidateTag(customerCacheTag)
|
|
380
|
+
return { success: true, error: null }
|
|
381
|
+
})
|
|
382
|
+
.catch((err) => {
|
|
383
|
+
return { success: false, error: err.toString() }
|
|
384
|
+
})
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export const setDefaultAddress = async (addressId: string) => {
|
|
388
|
+
const headers = {
|
|
389
|
+
...(await getAuthHeaders()),
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return sdk.store.customer
|
|
393
|
+
.updateAddress(addressId, { is_default_shipping: true }, {}, headers)
|
|
394
|
+
.then(async () => {
|
|
395
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
396
|
+
revalidateTag(customerCacheTag)
|
|
397
|
+
return { success: true, error: null }
|
|
398
|
+
})
|
|
399
|
+
.catch((err) => {
|
|
400
|
+
return { success: false, error: err.toString() }
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export async function initiateGoogleAuth() {
|
|
405
|
+
try {
|
|
406
|
+
const backendUrl = process.env.MEDUSA_BACKEND_URL || process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL
|
|
407
|
+
|
|
408
|
+
if (!backendUrl) {
|
|
409
|
+
return { error: "Backend URL not configured" }
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const response = await fetch(`${backendUrl}/auth/customer/google`, {
|
|
413
|
+
method: "POST",
|
|
414
|
+
headers: {
|
|
415
|
+
"Content-Type": "application/json",
|
|
416
|
+
},
|
|
417
|
+
cache: "no-store",
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
if (!response.ok) {
|
|
421
|
+
return { error: "Google auth failed" }
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const result = await response.json()
|
|
425
|
+
let redirectUrl = typeof result === "string" ? result : (result as any)?.location
|
|
426
|
+
|
|
427
|
+
if (!redirectUrl) {
|
|
428
|
+
return { error: "No redirect URL received from Google OAuth" }
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Fix redirect URL: Remove country code prefix from callback URL if present
|
|
432
|
+
// Example: https://chocomelon.in/in/auth/customer/google/callback -> https://chocomelon.in/auth/customer/google/callback
|
|
433
|
+
try {
|
|
434
|
+
const url = new URL(redirectUrl)
|
|
435
|
+
const pathParts = url.pathname.split('/').filter(Boolean)
|
|
436
|
+
|
|
437
|
+
// Check if path starts with country code pattern (2-3 letter code)
|
|
438
|
+
if (pathParts.length > 0 && /^[a-z]{2,3}$/i.test(pathParts[0])) {
|
|
439
|
+
// Check if next part is 'auth'
|
|
440
|
+
if (pathParts.length > 1 && pathParts[1] === 'auth') {
|
|
441
|
+
// Remove country code from path
|
|
442
|
+
pathParts.shift()
|
|
443
|
+
url.pathname = '/' + pathParts.join('/')
|
|
444
|
+
redirectUrl = url.toString()
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
} catch (e) {
|
|
448
|
+
// If URL parsing fails, use original redirectUrl
|
|
449
|
+
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return { redirectUrl }
|
|
453
|
+
} catch (error: any) {
|
|
454
|
+
return { error: error.message || "Internal server error" }
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export async function handleGoogleAuthCallback(params: Record<string, string>) {
|
|
459
|
+
try {
|
|
460
|
+
if (!params.code || !params.state) {
|
|
461
|
+
return { error: "Missing authentication parameters" }
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const backendUrl = process.env.MEDUSA_BACKEND_URL || process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL
|
|
465
|
+
|
|
466
|
+
if (!backendUrl) {
|
|
467
|
+
return { error: "Backend URL not configured" }
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const response = await fetch(
|
|
471
|
+
`${backendUrl}/auth/customer/google/callback?${new URLSearchParams(params).toString()}`,
|
|
472
|
+
{
|
|
473
|
+
method: "GET",
|
|
474
|
+
headers: {
|
|
475
|
+
"Content-Type": "application/json",
|
|
476
|
+
},
|
|
477
|
+
cache: "no-store",
|
|
478
|
+
}
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
if (!response.ok) {
|
|
482
|
+
return { error: `Authentication failed: ${response.status} - ${response.statusText}` }
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const data = await response.json()
|
|
486
|
+
const token = data.token || data
|
|
487
|
+
|
|
488
|
+
if (!token || typeof token !== "string") {
|
|
489
|
+
return { error: "No token received" }
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return { token }
|
|
493
|
+
} catch (error: any) {
|
|
494
|
+
return { error: error.message || "Internal server error" }
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export async function handleGoogleCallback(token: string, email?: string, first_name?: string, last_name?: string) {
|
|
499
|
+
try {
|
|
500
|
+
if (!token || typeof token !== "string") {
|
|
501
|
+
return { error: "Token is required" }
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
await setAuthToken(token)
|
|
505
|
+
await transferCart(token).catch(() => { })
|
|
506
|
+
|
|
507
|
+
if (email && typeof email === "string") {
|
|
508
|
+
const cookieStore = await cookies()
|
|
509
|
+
cookieStore.set("_medusa_customer_email", email, { maxAge: 60 * 60 * 24 * 7, path: "/" })
|
|
510
|
+
|
|
511
|
+
const headers = await getAuthHeaders()
|
|
512
|
+
if (headers && "authorization" in headers) {
|
|
513
|
+
try {
|
|
514
|
+
await sdk.store.customer.create(
|
|
515
|
+
{ email, ...(first_name && { first_name }), ...(last_name && { last_name }) },
|
|
516
|
+
{},
|
|
517
|
+
headers
|
|
518
|
+
)
|
|
519
|
+
} catch (err: any) {
|
|
520
|
+
if (!err.message?.includes("already exists") && !err.message?.includes("duplicate")) {
|
|
521
|
+
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
528
|
+
if (customerCacheTag) revalidateTag(customerCacheTag)
|
|
529
|
+
|
|
530
|
+
return { success: true }
|
|
531
|
+
} catch (error: any) {
|
|
532
|
+
return { error: error.message || "Internal server error" }
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export async function uploadProfileImage(formData: FormData) {
|
|
537
|
+
try {
|
|
538
|
+
const imageFile = formData.get("image") as File
|
|
539
|
+
|
|
540
|
+
if (!imageFile) {
|
|
541
|
+
return { error: "No image file provided" }
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Create new FormData with 'files' field as expected by the backend
|
|
545
|
+
const uploadFormData = new FormData()
|
|
546
|
+
uploadFormData.append("files", imageFile)
|
|
547
|
+
|
|
548
|
+
const { uploadStoreFiles } = await import("medusa-reviews-logic/server")
|
|
549
|
+
const uploadResult = await uploadStoreFiles(uploadFormData)
|
|
550
|
+
if (!uploadResult.success) {
|
|
551
|
+
return { error: uploadResult.error }
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const imageUrl = uploadResult.files?.[0]?.url
|
|
555
|
+
|
|
556
|
+
if (!imageUrl) {
|
|
557
|
+
throw new Error("No image URL returned from server")
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Fetch current customer to preserve existing metadata
|
|
561
|
+
const currentCustomer = await retrieveCustomer()
|
|
562
|
+
const existingMetadata = (currentCustomer?.metadata as Record<string, any>) || {}
|
|
563
|
+
const newMetadata = {
|
|
564
|
+
...existingMetadata,
|
|
565
|
+
profile_image_url: imageUrl,
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Update customer metadata
|
|
569
|
+
await updateCustomer({ metadata: newMetadata })
|
|
570
|
+
|
|
571
|
+
const customerCacheTag = await getCacheTag("customers")
|
|
572
|
+
if (customerCacheTag) {
|
|
573
|
+
revalidateTag(customerCacheTag)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return { success: true, imageUrl }
|
|
577
|
+
} catch (error: any) {
|
|
578
|
+
|
|
579
|
+
return { error: error.message || "Failed to upload image" }
|
|
580
|
+
}
|
|
581
|
+
}
|