mirror of
https://github.com/vercel/commerce.git
synced 2025-05-18 07:26:59 +00:00
feat: Add customer authentication
This commit is contained in:
parent
42a02d40ff
commit
0f381d4cc6
@ -1,6 +1,6 @@
|
||||
import { FC, useEffect, useState, useCallback } from 'react'
|
||||
import { Logo, Button, Input } from '@components/ui'
|
||||
import useLogin from '@framework/auth/use-login'
|
||||
import useLogin from '@commerce/auth/use-login'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import { validate } from 'email-validator'
|
||||
|
||||
@ -18,7 +18,6 @@ const LoginView: React.FC = () => {
|
||||
|
||||
const handleLogin = async (e: React.SyntheticEvent<EventTarget>) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (!dirty && !disabled) {
|
||||
setDirty(true)
|
||||
handleValidation()
|
||||
|
@ -17,7 +17,7 @@ const loginEndpoint: GetAPISchema<
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
debugger
|
||||
try {
|
||||
const body = req.body ?? {}
|
||||
return await handlers['login']({ ...ctx, body })
|
||||
|
@ -146,6 +146,7 @@ export const createEndpoint =
|
||||
options?: API['schema']['endpoint']['options']
|
||||
}
|
||||
): NextApiHandler => {
|
||||
debugger
|
||||
return getEndpoint(commerce, { ...endpoint, ...context })
|
||||
}
|
||||
|
||||
|
@ -1,5 +1 @@
|
||||
COMMERCE_PROVIDER=commercelayer
|
||||
|
||||
COMMERCELAYER_CLIENT_ID=
|
||||
COMMERCELAYER_ENDPOINT=
|
||||
COMMERCELAYER_MARKET_SCOPE=
|
@ -1,23 +1 @@
|
||||
# Commerce Layer Provider
|
||||
|
||||
⚠️ This provider is still a work in progress.
|
||||
|
||||
Before getting started, you should do the following:
|
||||
|
||||
- Create a Commerce Layer [developer account](https://commercelayer.io).
|
||||
- Create a new [organization](https://commercelayer.io/docs/data-model/users-and-organizations/) for your business.
|
||||
- Create an application with `sales_channel` kind.
|
||||
|
||||
Next, copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git):
|
||||
|
||||
```bash
|
||||
cp framework/commercelayer/.env.template .env.local
|
||||
```
|
||||
|
||||
Next, add the application credentials from your organization application dashboard in `.env.local`.
|
||||
|
||||
## Contribute
|
||||
|
||||
Our commitment to Open Source can be found [here](https://vercel.com/oss).
|
||||
|
||||
If you find an issue with the provider or want a new feature, feel free to open a PR or [create a new issue](https://github.com/vercel/commerce/issues).
|
||||
# Next.js Local Provider
|
||||
|
@ -1,18 +1,35 @@
|
||||
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||
import loginEndpoint from '@commerce/api/endpoints/login'
|
||||
import type { LoginSchema } from '../../../types/login'
|
||||
import type { CommercelayerAPI } from '../..'
|
||||
import login from './login'
|
||||
import { GetAPISchema } from '@commerce/api'
|
||||
import { CommerceAPIError } from '@commerce/api/utils/errors'
|
||||
import isAllowedOperation from '@commerce/api/utils/is-allowed-operation'
|
||||
import { LoginSchema } from '@commerce/types/login'
|
||||
|
||||
export type LoginAPI = GetAPISchema<CommercelayerAPI, LoginSchema>
|
||||
const loginEndpoint: GetAPISchema<
|
||||
any,
|
||||
LoginSchema<any>
|
||||
>['endpoint']['handler'] = async (ctx) => {
|
||||
const { req, res, handlers } = ctx
|
||||
debugger
|
||||
if (
|
||||
!isAllowedOperation(req, res, {
|
||||
POST: handlers['login'],
|
||||
})
|
||||
) {
|
||||
return
|
||||
}
|
||||
debugger
|
||||
try {
|
||||
const body = req.body ?? {}
|
||||
return await handlers['login']({ ...ctx, body })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
export type LoginEndpoint = LoginAPI['endpoint']
|
||||
const message =
|
||||
error instanceof CommerceAPIError
|
||||
? 'An unexpected error ocurred with the Commerce API'
|
||||
: 'An unexpected error ocurred'
|
||||
|
||||
export const handlers: LoginEndpoint['handlers'] = { login }
|
||||
res.status(500).json({ data: null, errors: [{ message }] })
|
||||
}
|
||||
}
|
||||
|
||||
const loginApi = createEndpoint<LoginAPI>({
|
||||
handler: loginEndpoint,
|
||||
handlers,
|
||||
})
|
||||
|
||||
export default loginApi
|
||||
export default loginEndpoint
|
||||
|
@ -1,47 +0,0 @@
|
||||
import { FetcherError } from '@commerce/utils/errors'
|
||||
import { getAccessToken } from '../../index'
|
||||
import type { LoginEndpoint } from '.'
|
||||
|
||||
const login: LoginEndpoint['handlers']['login'] = async ({
|
||||
res,
|
||||
body: { email, password },
|
||||
config,
|
||||
commerce,
|
||||
}) => {
|
||||
if (!(email && password)) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Invalid request' }],
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
const user = { email, password };
|
||||
const getToken = await getAccessToken(user);
|
||||
await config.apiFetch(`/api/customers/${getToken.customerId}`, {
|
||||
method: 'GET'
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof FetcherError &&
|
||||
/invalid credentials/i.test(error.message)) {
|
||||
return res.status(401).json({
|
||||
data: null,
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
'Cannot find an account that matches the provided credentials',
|
||||
code: 'INVALID_CREDENTIALS',
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
await commerce.login({ variables: { email, password }, config, res })
|
||||
|
||||
res.status(200).json({ data: null })
|
||||
}
|
||||
|
||||
export default login
|
@ -1,18 +1 @@
|
||||
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||
import signupEndpoint from '@commerce/api/endpoints/signup'
|
||||
import type { SignupSchema } from '../../../types/signup'
|
||||
import type { CommercelayerAPI } from '../..'
|
||||
import signup from './signup'
|
||||
|
||||
export type SignupAPI = GetAPISchema<CommercelayerAPI, SignupSchema>
|
||||
|
||||
export type SignupEndpoint = SignupAPI['endpoint']
|
||||
|
||||
export const handlers: SignupEndpoint['handlers'] = { signup }
|
||||
|
||||
const singupApi = createEndpoint<SignupAPI>({
|
||||
handler: signupEndpoint,
|
||||
handlers,
|
||||
})
|
||||
|
||||
export default singupApi
|
||||
export default function noopApi(...args: any[]): void {}
|
||||
|
@ -1,55 +0,0 @@
|
||||
import { CommercelayerApiError } from '../../utils/errors'
|
||||
import type { SignupEndpoint } from '.'
|
||||
|
||||
const signup: SignupEndpoint['handlers']['signup'] = async ({
|
||||
res,
|
||||
body: { email, password },
|
||||
config,
|
||||
commerce,
|
||||
}) => {
|
||||
if (!(email && password)) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Invalid request' }],
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
await config.apiFetch('/api/customers', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
data: {
|
||||
type: 'customers',
|
||||
attributes: {
|
||||
email: email,
|
||||
password: password,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof CommercelayerApiError && error.status === 422) {
|
||||
const inputEmail = error.data?.errors.meta.value
|
||||
|
||||
if ('code' in error.data?.errors) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [
|
||||
{
|
||||
message: `A user already exists with ${inputEmail}`,
|
||||
code: 'USER_EXISTS',
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
await commerce.login({ variables: { email, password }, res, config })
|
||||
|
||||
res.status(200).json({ data: null })
|
||||
}
|
||||
|
||||
export default signup
|
@ -1,7 +1,6 @@
|
||||
import type { CommerceAPI, CommerceAPIConfig } from '@commerce/api'
|
||||
import type { RequestInit, Response, Fetch } from '@vercel/fetch'
|
||||
import { getCommerceApi as commerceApi } from '@commerce/api'
|
||||
import createFetcher from './utils/fetch-api'
|
||||
import createFetcher from './utils/fetch-local'
|
||||
|
||||
import getAllPages from './operations/get-all-pages'
|
||||
import getPage from './operations/get-page'
|
||||
@ -11,63 +10,14 @@ import getAllProductPaths from './operations/get-all-product-paths'
|
||||
import getAllProducts from './operations/get-all-products'
|
||||
import getProduct from './operations/get-product'
|
||||
|
||||
import { getToken } from './utils/get-token'
|
||||
|
||||
export interface CommercelayerConfig extends Omit<CommerceAPIConfig, 'fetch'> {
|
||||
apiClientId: string
|
||||
apiToken: string
|
||||
apiFetch(
|
||||
query: string,
|
||||
endpoint: string,
|
||||
fetchOptions?: RequestInit,
|
||||
user?: UserCredentials
|
||||
): Promise<{ data: any; res: Response }>
|
||||
}
|
||||
|
||||
export type UserCredentials = {
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
const CLIENT_ID = process.env.COMMERCELAYER_CLIENT_ID
|
||||
const ENDPOINT = process.env.COMMERCELAYER_ENDPOINT
|
||||
const MARKET_SCOPE = process.env.COMMERCELAYER_MARKET_SCOPE
|
||||
|
||||
if (!CLIENT_ID) {
|
||||
throw new Error(
|
||||
`The environment variable COMMERCELAYER_CLIENT_ID is missing and it's required.`
|
||||
)
|
||||
}
|
||||
|
||||
if (!ENDPOINT) {
|
||||
throw new Error(
|
||||
`The environment variable COMMERCELAYER_ENDPOINT is missing and it's required.`
|
||||
)
|
||||
}
|
||||
|
||||
if (!MARKET_SCOPE) {
|
||||
throw new Error(
|
||||
`The environment variable COMMERCELAYER_MARKET_SCOPE is missing and it's required.`
|
||||
)
|
||||
}
|
||||
|
||||
export async function getAccessToken(user?: UserCredentials) {
|
||||
return await getToken({
|
||||
clientId: CLIENT_ID,
|
||||
endpoint: ENDPOINT,
|
||||
scope: MARKET_SCOPE,
|
||||
user,
|
||||
})
|
||||
}
|
||||
|
||||
const config: CommercelayerConfig = {
|
||||
commerceUrl: ENDPOINT,
|
||||
apiClientId: CLIENT_ID,
|
||||
export interface LocalConfig extends CommerceAPIConfig {}
|
||||
const config: LocalConfig = {
|
||||
commerceUrl: '',
|
||||
apiToken: '',
|
||||
cartCookie: '',
|
||||
customerCookie: '',
|
||||
cartCookieMaxAge: 2592000,
|
||||
apiToken: '',
|
||||
apiFetch: createFetcher(() => getCommerceApi().getConfig()),
|
||||
fetch: createFetcher(() => getCommerceApi().getConfig()),
|
||||
}
|
||||
|
||||
const operations = {
|
||||
@ -83,12 +33,10 @@ const operations = {
|
||||
export const provider = { config, operations }
|
||||
|
||||
export type Provider = typeof provider
|
||||
export type CommercelayerAPI<P extends Provider = Provider> = CommerceAPI<
|
||||
P | any
|
||||
>
|
||||
export type LocalAPI<P extends Provider = Provider> = CommerceAPI<P | any>
|
||||
|
||||
export function getCommerceApi<P extends Provider>(
|
||||
customProvider: P = provider as any
|
||||
): CommercelayerAPI<P> {
|
||||
): LocalAPI<P> {
|
||||
return commerceApi(customProvider as any)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
export type Page = { url: string }
|
||||
export type GetAllPagesResult = { pages: Page[] }
|
||||
import type { CommercelayerConfig } from '../index'
|
||||
import type { LocalConfig } from '../index'
|
||||
|
||||
export default function getAllPagesOperation() {
|
||||
function getAllPages({
|
||||
@ -8,7 +8,7 @@ export default function getAllPagesOperation() {
|
||||
preview,
|
||||
}: {
|
||||
url?: string
|
||||
config?: Partial<CommercelayerConfig>
|
||||
config?: Partial<LocalConfig>
|
||||
preview?: boolean
|
||||
}): Promise<GetAllPagesResult> {
|
||||
return Promise.resolve({
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Product } from '@commerce/types/product'
|
||||
import { GetAllProductsOperation } from '@commerce/types/product'
|
||||
import type { OperationContext } from '@commerce/api/operations'
|
||||
import type { CommercelayerConfig, Provider } from '../index'
|
||||
import type { LocalConfig, Provider } from '../index'
|
||||
import data from '../../data.json'
|
||||
|
||||
export default function getAllProductsOperation({
|
||||
@ -14,7 +14,7 @@ export default function getAllProductsOperation({
|
||||
}: {
|
||||
query?: string
|
||||
variables?: T['variables']
|
||||
config?: Partial<CommercelayerConfig>
|
||||
config?: Partial<LocalConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<{ products: Product[] | any[] }> {
|
||||
return {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { CommercelayerConfig } from '../index'
|
||||
import type { LocalConfig } from '../index'
|
||||
import { Product } from '@commerce/types/product'
|
||||
import { GetProductOperation } from '@commerce/types/product'
|
||||
import data from '../../data.json'
|
||||
@ -14,7 +14,7 @@ export default function getProductOperation({
|
||||
}: {
|
||||
query?: string
|
||||
variables?: T['variables']
|
||||
config?: Partial<CommercelayerConfig>
|
||||
config?: Partial<LocalConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<Product | {} | any> {
|
||||
return {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { OperationContext } from '@commerce/api/operations'
|
||||
import { Category } from '@commerce/types/site'
|
||||
import { CommercelayerConfig } from '../index'
|
||||
import { LocalConfig } from '../index'
|
||||
|
||||
export type GetSiteInfoResult<
|
||||
T extends { categories: any[]; brands: any[] } = {
|
||||
@ -17,7 +17,7 @@ export default function getSiteInfoOperation({}: OperationContext<any>) {
|
||||
}: {
|
||||
query?: string
|
||||
variables?: any
|
||||
config?: Partial<CommercelayerConfig>
|
||||
config?: Partial<LocalConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<GetSiteInfoResult> {
|
||||
return Promise.resolve({
|
||||
|
15
framework/commercelayer/api/utils/cookies.ts
Normal file
15
framework/commercelayer/api/utils/cookies.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import Cookies, { CookieAttributes } from 'js-cookie'
|
||||
|
||||
const setCookie = (
|
||||
name: string,
|
||||
token?: string,
|
||||
options?: CookieAttributes
|
||||
) => {
|
||||
if (!token) {
|
||||
Cookies.remove(name)
|
||||
} else {
|
||||
Cookies.set(name, token, options)
|
||||
}
|
||||
}
|
||||
|
||||
export default setCookie
|
@ -1,11 +0,0 @@
|
||||
// Email must start with and contain an alphanumeric character, contain a @ character, and . character
|
||||
export const validateEmail = (email: string) => {
|
||||
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return re.test(String(email).toLowerCase());
|
||||
}
|
||||
|
||||
// Passwords must be at least eight characters and must contain at least one uppercase letter, one lowercase letter, one number and one special character
|
||||
export const validatePassword = (password: string) => {
|
||||
const re = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
||||
return re.test(String(password).toLowerCase());
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import type { Response } from '@vercel/fetch'
|
||||
|
||||
export class CommercelayerApiError extends Error {
|
||||
status: number
|
||||
res: Response
|
||||
data: any
|
||||
|
||||
constructor(msg: string, res: Response, data?: any) {
|
||||
super(msg)
|
||||
this.name = 'CommercelayerApiError'
|
||||
this.status = res.status
|
||||
this.res = res
|
||||
this.data = data
|
||||
}
|
||||
}
|
||||
|
||||
export class CommercelayerNetworkError extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg)
|
||||
this.name = 'CommercelayerNetworkError'
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
import type { RequestInit } from '@vercel/fetch'
|
||||
import { FetcherError } from '@commerce/utils/errors'
|
||||
import { CommercelayerConfig, getAccessToken, UserCredentials } from '../index'
|
||||
import fetch from './fetch'
|
||||
|
||||
const fetchApi =
|
||||
(getConfig: () => CommercelayerConfig) =>
|
||||
async (
|
||||
query: string,
|
||||
endpoint: string,
|
||||
fetchOptions?: RequestInit,
|
||||
user?: UserCredentials
|
||||
) => {
|
||||
const config = getConfig()
|
||||
const getToken = await getAccessToken(user)
|
||||
const token = getToken?.accessToken
|
||||
const res = await fetch(config.commerceUrl + endpoint, {
|
||||
...fetchOptions,
|
||||
headers: {
|
||||
...fetchOptions?.headers,
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify([
|
||||
query,
|
||||
]),
|
||||
})
|
||||
|
||||
const json = await res.json()
|
||||
if (json.errors) {
|
||||
throw new FetcherError({
|
||||
errors: json.errors ?? [{ message: 'Failed to fetch Commerce Layer API' }],
|
||||
status: res.status,
|
||||
})
|
||||
}
|
||||
|
||||
return { data: json.data, res }
|
||||
}
|
||||
|
||||
export default fetchApi
|
35
framework/commercelayer/api/utils/fetch-local.ts
Normal file
35
framework/commercelayer/api/utils/fetch-local.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { FetcherError } from '@commerce/utils/errors'
|
||||
import type { GraphQLFetcher } from '@commerce/api'
|
||||
import type { LocalConfig } from '../index'
|
||||
import fetch from './fetch'
|
||||
|
||||
const fetchGraphqlApi: (getConfig: () => LocalConfig) => GraphQLFetcher =
|
||||
(getConfig) =>
|
||||
async (query: string, { variables, preview } = {}, fetchOptions) => {
|
||||
debugger
|
||||
const config = getConfig()
|
||||
const res = await fetch(config.commerceUrl, {
|
||||
...fetchOptions,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...fetchOptions?.headers,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
variables,
|
||||
}),
|
||||
})
|
||||
|
||||
const json = await res.json()
|
||||
if (json.errors) {
|
||||
throw new FetcherError({
|
||||
errors: json.errors ?? [{ message: 'Failed to fetch for API' }],
|
||||
status: res.status,
|
||||
})
|
||||
}
|
||||
|
||||
return { data: json.data, res }
|
||||
}
|
||||
|
||||
export default fetchGraphqlApi
|
@ -1,40 +0,0 @@
|
||||
import Cookies from 'js-cookie'
|
||||
import { getSalesChannelToken } from '@commercelayer/js-auth'
|
||||
|
||||
type GetTokenObj = {
|
||||
clientId?: string
|
||||
endpoint?: string
|
||||
scope?: string
|
||||
user?: any
|
||||
}
|
||||
|
||||
export async function getToken({
|
||||
clientId,
|
||||
endpoint,
|
||||
scope = 'market:all',
|
||||
user,
|
||||
}: GetTokenObj) {
|
||||
const getCookieToken = Cookies.get('clAccessToken')
|
||||
if (!getCookieToken && clientId && endpoint) {
|
||||
const auth = await getSalesChannelToken(
|
||||
{
|
||||
clientId,
|
||||
endpoint,
|
||||
scope,
|
||||
},
|
||||
user
|
||||
)
|
||||
Cookies.set('clAccessToken', auth?.accessToken as string, {
|
||||
// @ts-ignore
|
||||
expires: auth?.expires,
|
||||
})
|
||||
return auth
|
||||
? {
|
||||
accessToken: auth.accessToken,
|
||||
customerId: auth.data.owner_id,
|
||||
...auth.data,
|
||||
}
|
||||
: null
|
||||
}
|
||||
return { accessToken: getCookieToken }
|
||||
}
|
@ -1,40 +1,40 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import { MutationHook } from '@commerce/utils/types'
|
||||
import useLogin, { UseLogin } from '@commerce/auth/use-login'
|
||||
import type { LoginHook } from '../types/login'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import { getCustomerToken } from '@commercelayer/js-auth'
|
||||
import setCookie from '@framework/api/utils/cookies'
|
||||
|
||||
export default useLogin as UseLogin<typeof handler>
|
||||
|
||||
export const handler: MutationHook<LoginHook> = {
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
url: '/api/customers',
|
||||
method: 'GET',
|
||||
// query: 'login',
|
||||
url: '/customer',
|
||||
},
|
||||
async fetcher({ input: { email, password }, options, fetch }) {
|
||||
if (!(email && password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'An email address and password are required to login',
|
||||
message: 'An email and password are required to login',
|
||||
})
|
||||
}
|
||||
|
||||
return fetch({
|
||||
...options,
|
||||
body: { email, password },
|
||||
})
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { revalidate } = useCustomer()
|
||||
|
||||
return useCallback(
|
||||
async function login(input) {
|
||||
const data = await fetch({ input })
|
||||
await revalidate()
|
||||
return data
|
||||
const token = await getCustomerToken(
|
||||
{
|
||||
endpoint: process.env.NEXT_PUBLIC_COMMERCELAYER_ENDPOINT as string,
|
||||
clientId: process.env.NEXT_PUBLIC_COMMERCELAYER_CLIENT_ID as string,
|
||||
scope: process.env.NEXT_PUBLIC_COMMERCELAYER_MARKET_SCOPE as string,
|
||||
},
|
||||
[fetch, revalidate]
|
||||
{ username: email, password }
|
||||
)
|
||||
token &&
|
||||
setCookie('CL_TOKEN', token.accessToken, { expires: token.expires })
|
||||
return token
|
||||
},
|
||||
}
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() => {
|
||||
return async function login(input) {
|
||||
const data = await fetch({ input })
|
||||
return data
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -1,44 +1,19 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useSignup, { UseSignup } from '@commerce/auth/use-signup'
|
||||
import type { SignupHook } from '../types/signup'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import { MutationHook } from '@commerce/utils/types'
|
||||
import useSignup, { UseSignup } from '@commerce/auth/use-signup'
|
||||
|
||||
export default useSignup as UseSignup<typeof handler>
|
||||
|
||||
export const handler: MutationHook<SignupHook> = {
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
url: '/api/customers',
|
||||
method: 'POST',
|
||||
query: '',
|
||||
},
|
||||
async fetcher({
|
||||
input: { email, password },
|
||||
options,
|
||||
fetch,
|
||||
}) {
|
||||
if (!(email && password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'An email address and password are required to signup',
|
||||
})
|
||||
}
|
||||
|
||||
return fetch({
|
||||
...options,
|
||||
body: { email, password },
|
||||
})
|
||||
async fetcher() {
|
||||
return null
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { revalidate } = useCustomer()
|
||||
|
||||
return useCallback(
|
||||
async function signup(input) {
|
||||
const data = await fetch({ input })
|
||||
await revalidate()
|
||||
return data
|
||||
},
|
||||
[fetch, revalidate]
|
||||
)
|
||||
},
|
||||
}
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() =>
|
||||
() => {},
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { Fetcher } from '@commerce/utils/types'
|
||||
|
||||
export const fetcher: Fetcher = async () => {
|
||||
console.log('FETCHER')
|
||||
debugger
|
||||
const res = await fetch('./data.json')
|
||||
if (res.ok) {
|
||||
const { data } = await res.json()
|
||||
|
@ -1,13 +1,13 @@
|
||||
import * as React from 'react'
|
||||
import { ReactNode } from 'react'
|
||||
import { commerceLayerProvider } from './provider'
|
||||
import { localProvider } from './provider'
|
||||
import {
|
||||
CommerceConfig,
|
||||
CommerceProvider as CoreCommerceProvider,
|
||||
useCommerce as useCoreCommerce,
|
||||
} from '@commerce'
|
||||
|
||||
export const commerceLayerConfig: CommerceConfig = {
|
||||
export const localConfig: CommerceConfig = {
|
||||
locale: 'en-us',
|
||||
cartCookie: 'session',
|
||||
}
|
||||
@ -21,8 +21,8 @@ export function CommerceProvider({
|
||||
} & Partial<CommerceConfig>) {
|
||||
return (
|
||||
<CoreCommerceProvider
|
||||
provider={commerceLayerProvider}
|
||||
config={{ ...commerceLayerConfig, ...config }}
|
||||
provider={localProvider}
|
||||
config={{ ...localConfig, ...config }}
|
||||
>
|
||||
{children}
|
||||
</CoreCommerceProvider>
|
||||
|
@ -9,8 +9,8 @@ import { handler as useLogin } from './auth/use-login'
|
||||
import { handler as useLogout } from './auth/use-logout'
|
||||
import { handler as useSignup } from './auth/use-signup'
|
||||
|
||||
export type Provider = typeof commerceLayerProvider
|
||||
export const commerceLayerProvider = {
|
||||
export type Provider = typeof localProvider
|
||||
export const localProvider = {
|
||||
locale: 'en-us',
|
||||
cartCookie: 'session',
|
||||
fetcher: fetcher,
|
||||
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/cart'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/checkout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/common'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/customer'
|
@ -1,25 +0,0 @@
|
||||
import * as Cart from './cart'
|
||||
import * as Checkout from './checkout'
|
||||
import * as Common from './common'
|
||||
import * as Customer from './customer'
|
||||
import * as Login from './login'
|
||||
import * as Logout from './logout'
|
||||
import * as Page from './page'
|
||||
import * as Product from './product'
|
||||
import * as Signup from './signup'
|
||||
import * as Site from './site'
|
||||
import * as Wishlist from './wishlist'
|
||||
|
||||
export type {
|
||||
Cart,
|
||||
Checkout,
|
||||
Common,
|
||||
Customer,
|
||||
Login,
|
||||
Logout,
|
||||
Page,
|
||||
Product,
|
||||
Signup,
|
||||
Site,
|
||||
Wishlist,
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import * as Core from '@commerce/types/login'
|
||||
import { LoginBody, LoginTypes } from '@commerce/types/login'
|
||||
|
||||
export * from '@commerce/types/login'
|
||||
|
||||
export type LoginHook<T extends LoginTypes = LoginTypes> = {
|
||||
data: null
|
||||
actionInput: LoginBody
|
||||
fetcherInput: LoginBody
|
||||
body: T['body']
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/logout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/page'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/product'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/signup'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/site'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/wishlist'
|
@ -21,7 +21,7 @@
|
||||
"node": ">=14.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"@commercelayer/js-auth": "^2.0.6",
|
||||
"@commercelayer/js-auth": "^2.0.7",
|
||||
"@commercelayer/js-sdk": "^4.1.3",
|
||||
"@chec/commerce.js": "^2.8.0",
|
||||
"@react-spring/web": "^9.4.1",
|
||||
|
Loading…
x
Reference in New Issue
Block a user