diff --git a/.env b/.env index c5b0643c..c9511bb3 100644 --- a/.env +++ b/.env @@ -1,11 +1,10 @@ - VITE_APP_CTP_PROJECT_KEY=green-shop -VITE_APP_CTP_CLIENT_SECRET=wzD7hatuZ8giFcGL5h6htyGtNlAl1chR -VITE_APP_CTP_CLIENT_ID=LG1VRj2-lAjAdT3Wa0m9AXou +VITE_APP_CTP_CLIENT_SECRET=Nlpz1PodM9gnmEPEwHw91jhXv77qU4ww +VITE_APP_CTP_CLIENT_ID=BPJMQ0wyC-4dA_1sd04SlJQx VITE_APP_CTP_REGION=europe-west1 VITE_APP_CTP_AUTH_URL=https://auth.europe-west1.gcp.commercetools.com VITE_APP_CTP_API_URL=https://api.europe-west1.gcp.commercetools.com -VITE_APP_CTP_SCOPES=manage_orders:green-shop manage_payments:green-shop manage_types:green-shop view_shopping_lists:green-shop manage_customers:green-shop manage_my_orders:green-shop view_orders:green-shop manage_quotes:green-shop view_discount_codes:green-shop view_quote_requests:green-shop manage_order_edits:green-shop manage_products:green-shop manage_quote_requests:green-shop view_quotes:green-shop view_api_clients:green-shop view_order_edits:green-shop manage_shipping_methods:green-shop manage_cart_discounts:green-shop manage_my_shopping_lists:green-shop view_products:green-shop view_categories:green-shop manage_my_payments:green-shop manage_my_profile:green-shop manage_discount_codes:green-shop manage_categories:green-shop manage_shopping_lists:green-shop manage_extensions:green-shop +VITE_APP_CTP_SCOPES=manage_products:green-shop manage_sessions:green-shop manage_my_quotes:green-shop manage_customer_groups:green-shop manage_payments:green-shop manage_checkout_payment_intents:green-shop manage_api_clients:green-shop manage_order_edits:green-shop manage_project:green-shop manage_orders:green-shop create_anonymous_token:green-shop manage_business_units:green-shop manage_associate_roles:green-shop manage_product_selections:green-shop view_api_clients:green-shop view_audit_log:green-shop manage_cart_discounts:green-shop manage_my_shopping_lists:green-shop manage_connectors:green-shop manage_customers:green-shop introspect_oauth_tokens:green-shop manage_my_orders:green-shop manage_my_payments:green-shop manage_my_profile:green-shop manage_connectors_deployments:green-shop manage_audit_log:green-shop manage_attribute_groups:green-shop manage_my_quote_requests:green-shop manage_discount_codes:green-shop manage_categories:green-shop manage_extensions:green-shop manage_my_business_units:green-shop manage_import_containers:green-shop VITE_APP_DEFAULT_SEGMENT='/' VITE_APP_NEXT_SEGMENT=1 VITE_APP_PATH_SEGMENTS_TO_KEEP=0 diff --git a/package.json b/package.json index 1b0a7210..204f3752 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,10 @@ "@commercetools/sdk-client-v2": "^2.5.0", "@commercetools/sdk-middleware-auth": "^7.0.1", "@commercetools/sdk-middleware-http": "^7.0.4", + "@types/js-cookie": "^3.0.6", "autoprefixer": "^10.4.19", "isomorphic-fetch": "^3.0.0", + "js-cookie": "^3.0.5", "materialize-css": "^1.0.0-rc.2", "modern-normalize": "^2.0.0", "postcode-validator": "^3.8.20", diff --git a/src/shared/API/customer/model/CustomerModel.ts b/src/shared/API/customer/model/CustomerModel.ts index ae008726..872e5389 100644 --- a/src/shared/API/customer/model/CustomerModel.ts +++ b/src/shared/API/customer/model/CustomerModel.ts @@ -6,7 +6,7 @@ import type { Customer, CustomerPagedQueryResponse, CustomerSignInResult, - CustomerUpdateAction, + MyCustomerUpdateAction, } from '@commercetools/platform-sdk'; import getRoot, { type RootApi } from '../../sdk/root.ts'; @@ -24,51 +24,51 @@ export class CustomerModel { this.root = getRoot(); } - public static actionAddAddress(address: Address): CustomerUpdateAction { + public static actionAddAddress(address: Address): MyCustomerUpdateAction { return { action: 'addAddress', address: CustomerModel.adaptAddressToServer(address) }; } - public static actionEditAddress(address: Address): CustomerUpdateAction { + public static actionEditAddress(address: Address): MyCustomerUpdateAction { return { action: 'changeAddress', address: CustomerModel.adaptAddressToServer(address), addressId: address.id }; } - public static actionEditDateOfBirth(dateOfBirth: string): CustomerUpdateAction { + public static actionEditDateOfBirth(dateOfBirth: string): MyCustomerUpdateAction { return { action: 'setDateOfBirth', dateOfBirth }; } - public static actionEditDefaultBillingAddress(addressId: string): CustomerUpdateAction { + public static actionEditDefaultBillingAddress(addressId: string): MyCustomerUpdateAction { return { action: 'setDefaultBillingAddress', addressId }; } - public static actionEditDefaultShippingAddress(addressId: string): CustomerUpdateAction { + public static actionEditDefaultShippingAddress(addressId: string): MyCustomerUpdateAction { return { action: 'setDefaultShippingAddress', addressId }; } - public static actionEditEmail(email: string): CustomerUpdateAction { + public static actionEditEmail(email: string): MyCustomerUpdateAction { return { action: 'changeEmail', email }; } - public static actionEditFirstName(firstName: string): CustomerUpdateAction { + public static actionEditFirstName(firstName: string): MyCustomerUpdateAction { return { action: 'setFirstName', firstName }; } - public static actionEditLastName(lastName: string): CustomerUpdateAction { + public static actionEditLastName(lastName: string): MyCustomerUpdateAction { return { action: 'setLastName', lastName }; } - public static actionRemoveAddress(address: Address): CustomerUpdateAction { + public static actionRemoveAddress(address: Address): MyCustomerUpdateAction { return { action: 'removeAddress', addressId: address.id }; } - public static actionRemoveBillingAddress(address: Address): CustomerUpdateAction { + public static actionRemoveBillingAddress(address: Address): MyCustomerUpdateAction { return { action: 'removeBillingAddressId', addressId: address.id }; } - public static actionRemoveShippingAddress(address: Address): CustomerUpdateAction { + public static actionRemoveShippingAddress(address: Address): MyCustomerUpdateAction { return { action: 'removeShippingAddressId', addressId: address.id }; } - public static actionSetLocale(locale: string): CustomerUpdateAction { + public static actionSetLocale(locale: string): MyCustomerUpdateAction { return { action: 'setLocale', locale }; } @@ -181,18 +181,13 @@ export class CustomerModel { return this.getCustomerFromData(data) !== null; } - public async editCustomer(actions: CustomerUpdateAction[], customer: User): Promise { - const data = await this.root.editCustomer(actions, customer.version, customer.id); + public async editCustomer(actions: MyCustomerUpdateAction[], customer: User): Promise { + const data = await this.root.editCustomer(actions, customer.version); return this.getCustomerFromData(data); } public async editPassword(customer: User, currentPassword: string, newPassword: string): Promise { - const data = await this.root.editPassword(customer.id, customer.version, currentPassword, newPassword); - return this.getCustomerFromData(data); - } - - public async getCustomerByID(id: string): Promise { - const data = await this.root.getCustomerByID(id); + const data = await this.root.editPassword(customer.version, currentPassword, newPassword); return this.getCustomerFromData(data); } @@ -201,6 +196,10 @@ export class CustomerModel { return this.getCustomerFromData(data); } + public logout(): boolean { + return this.root.logoutUser(); + } + public async registerNewCustomer(userData: User): Promise { const data = await this.root.registrationUser(userData); return this.getCustomerFromData(data); diff --git a/src/shared/API/customer/tests/Customer.spec.ts b/src/shared/API/customer/tests/Customer.spec.ts index da249c1d..a1970053 100644 --- a/src/shared/API/customer/tests/Customer.spec.ts +++ b/src/shared/API/customer/tests/Customer.spec.ts @@ -74,8 +74,22 @@ describe('Checking Customer Model', () => { expect(customer).toHaveProperty('version'); }); + it('should authenticate the customer', async () => { + auth = await customerModel.authCustomer(user); + expect(typeof auth).toBe('object'); + expect(auth).toHaveProperty('addresses'); + expect(auth).toHaveProperty('defaultBillingAddressId'); + expect(auth).toHaveProperty('defaultShippingAddressId'); + expect(auth).toHaveProperty('email'); + expect(auth).toHaveProperty('firstName'); + expect(auth).toHaveProperty('id'); + expect(auth).toHaveProperty('lastName'); + expect(auth).toHaveProperty('password'); + expect(auth).toHaveProperty('version'); + }); + it('should edit the customer', async () => { - if (customer) { + if (auth) { editCustomer = await customerModel.editCustomer( [ CustomerModel.actionEditFirstName('John'), @@ -84,7 +98,7 @@ describe('Checking Customer Model', () => { CustomerModel.actionAddAddress(address), CustomerModel.actionEditDateOfBirth('1990-01-01'), ], - customer, + auth, ); expect(typeof editCustomer).toBe('object'); expect(editCustomer).toHaveProperty('addresses'); @@ -128,39 +142,9 @@ describe('Checking Customer Model', () => { } }); - it('should found the customer by id', async () => { - if (customer) { - const getCustomer = await customerModel.getCustomerByID(customer.id); - expect(typeof getCustomer).toBe('object'); - expect(getCustomer).toHaveProperty('addresses'); - expect(getCustomer).toHaveProperty('defaultBillingAddressId'); - expect(getCustomer).toHaveProperty('defaultShippingAddressId'); - expect(getCustomer).toHaveProperty('email'); - expect(getCustomer).toHaveProperty('firstName'); - expect(getCustomer).toHaveProperty('id'); - expect(getCustomer).toHaveProperty('lastName'); - expect(getCustomer).toHaveProperty('password'); - expect(getCustomer).toHaveProperty('version'); - } - }); - - it('should authenticate the customer', async () => { - auth = await customerModel.authCustomer({ email: 'test-test-test@example.com', password: 'Qqq11' }); - expect(typeof auth).toBe('object'); - expect(auth).toHaveProperty('addresses'); - expect(auth).toHaveProperty('defaultBillingAddressId'); - expect(auth).toHaveProperty('defaultShippingAddressId'); - expect(auth).toHaveProperty('email'); - expect(auth).toHaveProperty('firstName'); - expect(auth).toHaveProperty('id'); - expect(auth).toHaveProperty('lastName'); - expect(auth).toHaveProperty('password'); - expect(auth).toHaveProperty('version'); - }); - it('should edit the customer password', async () => { - if (auth) { - editPassword = await customerModel.editPassword(auth, 'Qqq11', 'Qqq11'); + if (editAddress) { + editPassword = await customerModel.editPassword(editAddress, 'Qqq11', 'Qqq11'); expect(typeof editPassword).toBe('object'); expect(editPassword).toHaveProperty('addresses'); expect(editPassword).toHaveProperty('defaultBillingAddressId'); diff --git a/src/shared/API/sdk/client.ts b/src/shared/API/sdk/client.ts index 365ff021..bbb8766a 100644 --- a/src/shared/API/sdk/client.ts +++ b/src/shared/API/sdk/client.ts @@ -1,34 +1,167 @@ +import type { UserCredentials } from '@/shared/types/user'; + +import { type ByProjectKeyRequestBuilder, createApiBuilderFromCtpClient } from '@commercetools/platform-sdk'; import { type AuthMiddlewareOptions, type Client, ClientBuilder, type HttpMiddlewareOptions, + type Middleware, + type PasswordAuthMiddlewareOptions, + type RefreshAuthMiddlewareOptions, + createAuthForAnonymousSessionFlow, + createAuthForClientCredentialsFlow, + createAuthForPasswordFlow, } from '@commercetools/sdk-client-v2'; +import { TokenType } from '../types/type.ts'; +import getTokenCache from './token-cache/token-cache.ts'; + const URL_AUTH = 'https://auth.europe-west1.gcp.commercetools.com'; const URL_HTTP = 'https://api.europe-west1.gcp.commercetools.com'; +const USE_SAVE_TOKEN = false; const httpMiddlewareOptions: HttpMiddlewareOptions = { fetch, host: URL_HTTP, }; -export default function client(projectKey: string, clientID: string, clientSecret: string, scopes: string): Client { - const scopesArr = scopes.split(','); - const authMiddlewareOptions: AuthMiddlewareOptions = { - credentials: { - clientId: clientID, - clientSecret, - }, - fetch, - host: URL_AUTH, - projectKey, - scopes: scopesArr, - }; - - return new ClientBuilder() - .withProjectKey(projectKey) - .withHttpMiddleware(httpMiddlewareOptions) - .withClientCredentialsFlow(authMiddlewareOptions) - .build(); +export default class ApiClient { + private adminConnection: ByProjectKeyRequestBuilder; + + private anonymConnection: ByProjectKeyRequestBuilder | null = null; + + private authConnection: ByProjectKeyRequestBuilder | null = null; + + private clientID: string; + + private clientSecret: string; + + private isAuth = false; + + private projectKey: string; + + private scopes: string[]; + + constructor(projectKey: string, clientID: string, clientSecret: string, scopes: string) { + this.projectKey = projectKey; + this.clientID = clientID; + this.clientSecret = clientSecret; + this.scopes = scopes.split(','); + + this.anonymConnection = this.createAnonymConnection(); + + const adminOptions = createAuthForClientCredentialsFlow({ + credentials: { + clientId: this.clientID, + clientSecret: this.clientSecret, + }, + fetch, + host: URL_AUTH, + projectKey: this.projectKey, + scopes: this.scopes, + }); + + const adminClient = this.getAdminClient(adminOptions); + + this.adminConnection = this.getConnection(adminClient); + } + + private getAdminClient(middleware: Middleware): Client { + return new ClientBuilder() + .withProjectKey(this.projectKey) + .withHttpMiddleware(httpMiddlewareOptions) + .withMiddleware(middleware) + .build(); + } + + private getAuthOption(credentials: UserCredentials): Middleware { + const { email, password } = credentials || { email: '', password: '' }; + const defaultOptions = this.getDefaultOptions(TokenType.AUTH); + const authOptions: PasswordAuthMiddlewareOptions = { + ...defaultOptions, + credentials: { + ...defaultOptions.credentials, + user: { + password, + username: email, + }, + }, + }; + return createAuthForPasswordFlow(authOptions); + } + + private getClient(middleware: Middleware, token: TokenType): Client { + const defaultOptions = this.getDefaultOptions(token); + const opt: RefreshAuthMiddlewareOptions = { + ...defaultOptions, + refreshToken: token, + }; + return new ClientBuilder() + .withProjectKey(this.projectKey) + .withHttpMiddleware(httpMiddlewareOptions) + .withMiddleware(middleware) + .withRefreshTokenFlow(opt) + .build(); + } + + private getConnection(client: Client): ByProjectKeyRequestBuilder { + return createApiBuilderFromCtpClient(client).withProjectKey({ projectKey: this.projectKey }); + } + + private getDefaultOptions(tokenType: TokenType): AuthMiddlewareOptions { + return { + credentials: { + clientId: this.clientID, + clientSecret: this.clientSecret, + }, + fetch, + host: URL_AUTH, + projectKey: this.projectKey, + scopes: this.scopes, + tokenCache: USE_SAVE_TOKEN ? getTokenCache(tokenType) : undefined, + }; + } + + public adminRoot(): ByProjectKeyRequestBuilder { + return this.adminConnection; + } + + public apiRoot(): ByProjectKeyRequestBuilder { + let client = this.authConnection && this.isAuth ? this.authConnection : this.anonymConnection; + if (!client) { + client = this.createAnonymConnection(); + } + return client; + } + + public approveAuth(): boolean { + this.isAuth = true; + return this.isAuth; + } + + public createAnonymConnection(): ByProjectKeyRequestBuilder { + const defaultOptions = this.getDefaultOptions(TokenType.ANONYM); + const anonymOptions = createAuthForAnonymousSessionFlow(defaultOptions); + const anonymClient = this.getClient(anonymOptions, TokenType.ANONYM); + this.anonymConnection = this.getConnection(anonymClient); + + return this.anonymConnection; + } + + public createAuthConnection(credentials: UserCredentials): ByProjectKeyRequestBuilder { + if (!this.authConnection || (this.authConnection && !this.isAuth)) { + const authOptions = this.getAuthOption(credentials); + const authClient = this.getClient(authOptions, TokenType.AUTH); + this.authConnection = this.getConnection(authClient); + } + return this.authConnection; + } + + public deleteAuthConnection(): boolean { + this.authConnection = null; + this.isAuth = false; + this.anonymConnection = this.createAnonymConnection(); + return this.authConnection === null; + } } diff --git a/src/shared/API/sdk/root.ts b/src/shared/API/sdk/root.ts index af0afc5b..04caa26a 100644 --- a/src/shared/API/sdk/root.ts +++ b/src/shared/API/sdk/root.ts @@ -2,24 +2,22 @@ import type { User, UserCredentials } from '@/shared/types/user.ts'; import { DEFAULT_PAGE, MAX_PRICE, MIN_PRICE, PRODUCT_LIMIT } from '@/shared/constants/product.ts'; import { - type ByProjectKeyRequestBuilder, type CategoryPagedQueryResponse, type ClientResponse, type Customer, type CustomerPagedQueryResponse, type CustomerSignInResult, - type CustomerUpdateAction, + type MyCustomerUpdateAction, type Product, type ProductProjectionPagedQueryResponse, type ProductProjectionPagedSearchResponse, - createApiBuilderFromCtpClient, } from '@commercetools/platform-sdk'; -import { type Client } from '@commercetools/sdk-client-v2'; - -import type { OptionsRequest } from '../types/type.ts'; import makeSortRequest from '../product/utils/sort.ts'; -import client from './client.ts'; +import { type OptionsRequest, TokenType } from '../types/type.ts'; +import { isErrorResponse } from '../types/validation.ts'; +import ApiClient from './client.ts'; +import getTokenCache from './token-cache/token-cache.ts'; type Nullable = T | null; @@ -36,9 +34,7 @@ const clientID = import.meta.env.VITE_APP_CTP_CLIENT_ID; const clientSecret = import.meta.env.VITE_APP_CTP_CLIENT_SECRET; export class RootApi { - private client: Client; - - private connection: ByProjectKeyRequestBuilder; + private client: ApiClient; private credentials: Credentials; @@ -50,60 +46,56 @@ export class RootApi { scopes, }; - this.client = client( + this.client = new ApiClient( this.credentials.projectKey || '', this.credentials.clientID || '', this.credentials.clientSecret || '', this.credentials.scopes || '', ); - - this.connection = this.root(this.client, projectKey); - } - - private root(client: Client, projectKey: string): ByProjectKeyRequestBuilder { - return createApiBuilderFromCtpClient(client).withProjectKey({ projectKey }); } public async authenticateUser(userLoginData: UserCredentials): Promise> { - const data = await this.connection.login().post({ body: userLoginData }).execute(); + this.client.createAuthConnection(userLoginData); + const data = await this.client.apiRoot().me().login().post({ body: userLoginData }).execute(); + if (!isErrorResponse(data)) { + this.client.approveAuth(); + } return data; } public async deleteCustomer(ID: string, version: number): Promise> { - const data = await this.connection.customers().withId({ ID }).delete({ queryArgs: { version } }).execute(); + const data = await this.client.adminRoot().customers().withId({ ID }).delete({ queryArgs: { version } }).execute(); + this.logoutUser(); return data; } - public async editCustomer( - actions: CustomerUpdateAction[], - version: number, - ID: string, - ): Promise> { - const data = await this.connection.customers().withId({ ID }).post({ body: { actions, version } }).execute(); + public async editCustomer(actions: MyCustomerUpdateAction[], version: number): Promise> { + const data = await this.client.apiRoot().me().post({ body: { actions, version } }).execute(); return data; } public async editPassword( - id: string, version: number, currentPassword: string, newPassword: string, ): Promise> { - const data = await this.connection - .customers() + const data = await this.client + .apiRoot() + .me() .password() - .post({ body: { currentPassword, id, newPassword, version } }) + .post({ body: { currentPassword, newPassword, version } }) .execute(); return data; } public async getCategories(): Promise> { - const data = await this.connection.categories().get().execute(); + const data = await this.client.apiRoot().categories().get().execute(); return data; } public async getCategoriesProductCount(): Promise> { - const data = await this.connection + const data = await this.client + .apiRoot() .productProjections() .search() .get({ @@ -117,20 +109,17 @@ export class RootApi { } public async getCustomerByEmail(email: string): Promise> { - const data = await this.connection + const data = await this.client + .apiRoot() .customers() .get({ queryArgs: { where: `email="${email}"` } }) .execute(); return data; } - public async getCustomerByID(ID: string): Promise> { - const data = await this.connection.customers().withId({ ID }).get().execute(); - return data; - } - public async getPriceRange(): Promise> { - const data = await this.connection + const data = await this.client + .apiRoot() .productProjections() .search() .get({ @@ -144,14 +133,15 @@ export class RootApi { } public async getProductByID(ID: string): Promise> { - const data = await this.connection.products().withId({ ID }).get().execute(); + const data = await this.client.apiRoot().products().withId({ ID }).get().execute(); return data; } public async getProducts(options?: OptionsRequest): Promise> { const { filter, limit = PRODUCT_LIMIT, page = DEFAULT_PAGE, search, sort } = options || {}; - const data = await this.connection + const data = await this.client + .apiRoot() .productProjections() .search() .get({ @@ -171,7 +161,8 @@ export class RootApi { } public async getSizeProductCount(): Promise> { - const data = await this.connection + const data = await this.client + .apiRoot() .productProjections() .search() .get({ @@ -184,12 +175,22 @@ export class RootApi { return data; } + public logoutUser(): boolean { + getTokenCache(TokenType.AUTH).clear(); + return this.client.deleteAuthConnection(); + } + public async registrationUser(userData: User): Promise> { const userCredentials = { email: userData.email, password: userData.password, }; - const data = await this.connection.customers().post({ body: userCredentials }).execute(); + + const data = await this.client.apiRoot().me().signup().post({ body: userCredentials }).execute(); + if (!isErrorResponse(data)) { + this.client.createAuthConnection(userData); + this.client.approveAuth(); + } return data; } } diff --git a/src/shared/API/sdk/token-cache/token-cache.ts b/src/shared/API/sdk/token-cache/token-cache.ts new file mode 100644 index 00000000..1cbc74d5 --- /dev/null +++ b/src/shared/API/sdk/token-cache/token-cache.ts @@ -0,0 +1,66 @@ +import type { TokenCache, TokenStore } from '@commercetools/sdk-client-v2'; + +import Cookies from 'js-cookie'; + +import { TokenType } from '../../types/type.ts'; + +export class MyTokenCache implements TokenCache { + private myCache: TokenStore = { + expirationTime: 0, + refreshToken: undefined, + token: '', + }; + + private name: string; + + constructor(name: string) { + this.name = name; + const token = Cookies.get(`${this.name}-token`); + const expirationTime = Number(Cookies.get(`${this.name}-expirationTime`)); + const refreshToken = Cookies.get(`${this.name}-refreshToken`); + if (token && refreshToken) { + this.myCache = { expirationTime, refreshToken, token }; + } + } + + private saveToken(): void { + if (this.myCache.token) { + Cookies.set(`${this.name}-token`, this.myCache.token); + Cookies.set(`${this.name}-expirationTime`, this.myCache.expirationTime.toString()); + Cookies.set(`${this.name}-refreshToken`, this.myCache.refreshToken || ''); + } + } + + public clear(): void { + this.myCache = { + expirationTime: 0, + refreshToken: undefined, + token: '', + }; + Cookies.remove(`${this.name}-token`); + Cookies.remove(`${this.name}-expirationTime`); + Cookies.remove(`${this.name}-refreshToken`); + } + + public get(): TokenStore { + return this.myCache; + } + + public isExist(): boolean { + return this.myCache.token !== '' || this.myCache.refreshToken !== undefined; + } + + public set(newCache: TokenStore): void { + Object.assign(this.myCache, newCache); + this.saveToken(); + } +} + +const createTokenCache = (name: string): MyTokenCache => new MyTokenCache(name); + +const anonymTokenCache = createTokenCache(TokenType.ANONYM); +const authTokenCache = createTokenCache(TokenType.AUTH); + +export default function getTokenCache(tokenType?: TokenType): MyTokenCache { + return tokenType === TokenType.AUTH || authTokenCache.isExist() ? authTokenCache : anonymTokenCache; +} diff --git a/src/shared/API/types/type.ts b/src/shared/API/types/type.ts index ef89c925..fc4c83d1 100644 --- a/src/shared/API/types/type.ts +++ b/src/shared/API/types/type.ts @@ -5,6 +5,11 @@ export const Attribute = { SIZE: 'size', }; +export enum TokenType { + ANONYM = 'anonym', + AUTH = 'auth', +} + export enum FilterFields { CATEGORY = 'categories.id', NEW_ARRIVAL = 'variants.attributes.new_arrival:true', diff --git a/src/shared/API/types/validation.ts b/src/shared/API/types/validation.ts index d5bd9a31..ce2bf6ec 100644 --- a/src/shared/API/types/validation.ts +++ b/src/shared/API/types/validation.ts @@ -5,6 +5,7 @@ import type { Customer, CustomerPagedQueryResponse, CustomerSignInResult, + ErrorResponse, FacetRange, FacetTerm, LocalizedString, @@ -180,3 +181,15 @@ export function isFacetTerm(data: unknown): data is FacetTerm { ('count' in data || 'productCount' in data), ); } + +export function isErrorResponse(data: unknown): data is ErrorResponse { + return Boolean( + typeof data === 'object' && + data && + 'statusCode' in data && + typeof data.statusCode === 'number' && + data.statusCode >= 400 && + 'message' in data && + typeof data.message === 'string', + ); +} diff --git a/src/widgets/Header/model/HeaderModel.ts b/src/widgets/Header/model/HeaderModel.ts index 06adcbbe..b90a35c8 100644 --- a/src/widgets/Header/model/HeaderModel.ts +++ b/src/widgets/Header/model/HeaderModel.ts @@ -1,6 +1,7 @@ import type RouterModel from '@/app/Router/model/RouterModel.ts'; import NavigationModel from '@/entities/Navigation/model/NavigationModel.ts'; +import getCustomerModel from '@/shared/API/customer/model/CustomerModel.ts'; import getStore from '@/shared/Store/Store.ts'; import { setCurrentUser } from '@/shared/Store/actions.ts'; import observeStore, { selectCurrentUser } from '@/shared/Store/observer.ts'; @@ -46,6 +47,7 @@ class HeaderModel { private logoutHandler(): boolean { localStorage.clear(); getStore().dispatch(setCurrentUser(null)); + getCustomerModel().logout(); this.router.navigateTo(PAGE_ID.LOGIN_PAGE); return true; }