Shop registration works now
This commit is contained in:
parent
a0d238c840
commit
d87c280109
@ -5,7 +5,7 @@ from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from jwt.exceptions import InvalidTokenError
|
||||
from pydantic import ValidationError
|
||||
from sqlmodel import Session
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.core import security
|
||||
from app.core.config import settings
|
||||
@ -19,7 +19,7 @@ reusable_oauth2 = OAuth2PasswordBearer(
|
||||
|
||||
SessionDep = Annotated[Session, Depends(get_session)]
|
||||
TokenDep = Annotated[str, Depends(reusable_oauth2)]
|
||||
LoginDep = Annotated[OAuth2PasswordRequestForm, Depends()]
|
||||
LoginDep = Annotated[OAuth2PasswordRequestForm, Depends(OAuth2PasswordRequestForm)]
|
||||
|
||||
|
||||
def get_current_user(session: SessionDep, token: TokenDep) -> User:
|
||||
@ -33,11 +33,10 @@ def get_current_user(session: SessionDep, token: TokenDep) -> User:
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Could not validate credentials",
|
||||
)
|
||||
user = session.get(User, {"uuid": token_data.sub})
|
||||
stmt = select(User).where(User.uuid == token_data.sub)
|
||||
user = session.exec(stmt).one_or_none()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
if not user.is_active:
|
||||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
return user
|
||||
|
||||
|
||||
|
@ -30,7 +30,7 @@ def login_access_token(
|
||||
"""
|
||||
user = None
|
||||
user = user_crud.authenticate(
|
||||
session=session, email=form_data.username, password=form_data.password, shop_id=None
|
||||
session=session, email=form_data.username, password=form_data.password, shop_uuid=None
|
||||
)
|
||||
if not user:
|
||||
raise HTTPException(status_code=400, detail="Incorrect email or password")
|
||||
@ -39,6 +39,6 @@ def login_access_token(
|
||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
return Token(
|
||||
access_token=security.create_access_token(
|
||||
user.id, expires_delta=access_token_expires
|
||||
user.uuid, expires_delta=access_token_expires
|
||||
)
|
||||
)
|
||||
|
@ -1,18 +1,18 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
|
||||
from app.api.dependencies import CurrentOwnerUser, SessionDep
|
||||
from app.schemas.shop_schemas import ShopCreate
|
||||
from app.schemas.user_schemas import Token
|
||||
from app.crud.shop_crud import create_shop
|
||||
|
||||
router = APIRouter(prefix="/shop", tags=["Shop"])
|
||||
router = APIRouter(prefix="/shop", tags=["Dashboard"])
|
||||
|
||||
|
||||
@router.post("")
|
||||
@router.post("", response_model=bool, status_code=201)
|
||||
def register_new_shop(
|
||||
session: SessionDep,
|
||||
current_user: CurrentOwnerUser,
|
||||
shop_data: ShopCreate
|
||||
) -> Token:
|
||||
"""
|
||||
OAuth2 compatible token login, get an access token for future requests
|
||||
"""
|
||||
create_shop(session, shop_data, current_user)
|
||||
return True
|
||||
|
@ -1,38 +1,35 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from fastapi import APIRouter, Body, HTTPException, status
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from app.api.dependencies import SessionDep
|
||||
from app.api.dependencies import CurrentOwnerUser, SessionDep
|
||||
from app.crud import user_crud
|
||||
from app.database.models.user_model import UserRole
|
||||
from app.schemas.user_schemas import UserRegister
|
||||
from app.schemas.user_schemas import UserRegister, UserUpdate
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
INTERNAL_SERVER_ERROR = HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error")
|
||||
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/user",
|
||||
tags=["User"]
|
||||
tags=["User", "Dashboard"]
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/delete", summary="Delete user")
|
||||
async def delete_user():
|
||||
raise NotImplementedError("delete_user() needs to be implemented.")
|
||||
@router.get("", summary="Get information about currently logged in user")
|
||||
async def get_user(session: SessionDep, current_user: CurrentOwnerUser):
|
||||
pass
|
||||
|
||||
|
||||
@router.delete("/logout", summary="User logout")
|
||||
async def logout():
|
||||
raise NotImplementedError("logout() needs to be implemented.")
|
||||
|
||||
|
||||
@router.post("/register", summary="Register new user")
|
||||
@router.post("", summary="Register new user", status_code=status.HTTP_201_CREATED, response_model=bool)
|
||||
async def register(session: SessionDep, user_data: UserRegister):
|
||||
try:
|
||||
user_crud.create_user(session, user_data, None, UserRole.OWNER)
|
||||
return {"detail": "Registered succeesfully"}
|
||||
return True
|
||||
except IntegrityError as e:
|
||||
field_mapping = {"uuid": "email"} # If a UUID is duplicate, it means email is in use
|
||||
|
||||
@ -49,10 +46,17 @@ async def register(session: SessionDep, user_data: UserRegister):
|
||||
detail=detail
|
||||
)
|
||||
except Exception as e:
|
||||
if isinstance(e, HTTPException):
|
||||
raise
|
||||
logger.error(e)
|
||||
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to register")
|
||||
raise INTERNAL_SERVER_ERROR
|
||||
|
||||
|
||||
@router.put("/update", summary="Update user details")
|
||||
async def update_user(data: dict = Body(...)):
|
||||
raise NotImplementedError("update_user() needs to be implemented.")
|
||||
@router.delete("", summary="Delete user")
|
||||
async def delete_user():
|
||||
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="delete_user() not implemented")
|
||||
|
||||
|
||||
@router.put("", summary="Update user details")
|
||||
async def update_user(data: UserUpdate):
|
||||
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="update_user() not implemented")
|
||||
|
@ -34,6 +34,14 @@ class ShopBusinessHours(BaseModel):
|
||||
hours: list[ShopBusinessHourEntry]
|
||||
|
||||
|
||||
class ShopAddress(SQLModel):
|
||||
street: str = Field(max_length=255, nullable=False)
|
||||
city: str = Field(max_length=100, nullable=False)
|
||||
state: str = Field(max_length=100, nullable=True)
|
||||
postal_code: str = Field(max_length=20, nullable=False)
|
||||
country: str = Field(max_length=100, nullable=False)
|
||||
|
||||
|
||||
class Shop(SQLModel, table=True):
|
||||
__tablename__ = 'shop'
|
||||
|
||||
@ -48,16 +56,11 @@ class Shop(SQLModel, table=True):
|
||||
logo: Optional[str] = Field(max_length=100)
|
||||
contact_email: str = Field(max_length=128, nullable=False, unique=True)
|
||||
phone_number: str = Field(max_length=15, nullable=False)
|
||||
address: str = Field(nullable=True)
|
||||
address: ShopAddress = Field(sa_column=Column(JSONB, nullable=False, default=lambda: {}))
|
||||
business_hours: ShopBusinessHours = Field(sa_column=Column(JSONB, nullable=False, default=lambda: {}))
|
||||
links: ShopLinks = Field(sa_column=Column(JSONB, nullable=False, default=lambda: {}))
|
||||
currency: str = Field(max_length=3, nullable=False)
|
||||
|
||||
business_hours: ShopBusinessHours = Field(
|
||||
sa_column=Column(JSONB, nullable=False, default=lambda: {})
|
||||
)
|
||||
links: ShopLinks = Field(
|
||||
sa_column=Column(JSONB, nullable=False, default=lambda: {})
|
||||
)
|
||||
|
||||
owner: Optional["User"] = Relationship(
|
||||
back_populates='owned_shop',
|
||||
sa_relationship_kwargs={"foreign_keys": "[Shop.owner_id]"}
|
||||
@ -68,10 +71,11 @@ class Shop(SQLModel, table=True):
|
||||
sa_relationship_kwargs={"foreign_keys": "[User.shop_id]"}
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
CheckConstraint("business_hours ? 'hours'", name="check_business_hours_keys"),
|
||||
CheckConstraint("links ? 'links'", name="check_links_keys"),
|
||||
)
|
||||
# __table_args__ = (
|
||||
# CheckConstraint("business_hours ? 'hours'", name="check_business_hours_keys"),
|
||||
# CheckConstraint("links ? 'links'", name="check_links_keys"),
|
||||
# CheckConstraint("address ? 'address'", name="check_address_keys")
|
||||
# )
|
||||
|
||||
|
||||
__all__ = ["Shop", "ShopBusinessHours", "ShopLinks", "ShopStatus"]
|
||||
|
@ -1,8 +1,14 @@
|
||||
|
||||
from pydantic import EmailStr
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from app.database.models.shop_model import ShopAddress
|
||||
|
||||
|
||||
class ShopCreate(SQLModel):
|
||||
name: str = Field(max_length=100, nullable=False, unique=True)
|
||||
description: str = Field(max_length=500, nullable=False)
|
||||
currency: str = Field(max_length=3, nullable=False)
|
||||
contact_email: EmailStr = Field()
|
||||
contact_phone_number: str = Field(min_length=2, max_length=16, schema_extra={"pattern": r'^\+[1-9]\d{1,14}$'})
|
||||
address: ShopAddress
|
||||
|
@ -1,14 +1,15 @@
|
||||
import re
|
||||
from pydantic import EmailStr, model_validator
|
||||
from sqlmodel import Field, SQLModel
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, EmailStr, Field, model_validator
|
||||
|
||||
|
||||
class UserRegister(SQLModel):
|
||||
class UserRegister(BaseModel):
|
||||
username: str = Field(min_length=3, max_length=64)
|
||||
email: EmailStr = Field()
|
||||
phone_number: str = Field(min_length=2, max_length=16, schema_extra={"pattern": r'^\+[1-9]\d{1,14}$'})
|
||||
password: str = Field(min_length=8, max_length=128,
|
||||
description="Password must be at least 8 and at most 128 characters long, contain a lower case, upper case letter, a number and a special character (#?!@$ %^&*-)")
|
||||
phone_number: str = Field(min_length=2, max_length=16, pattern=r'^\+[1-9]\d{1,14}$')
|
||||
password: str = Field(min_length=8, max_length=128, examples=["Abc123#!"],
|
||||
description="Password must conform to this regex: \n```\n^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$\n```")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_using_regex(self):
|
||||
@ -20,11 +21,29 @@ class UserRegister(SQLModel):
|
||||
if not re.match(password_regex, self.password):
|
||||
raise ValueError("Password is too weak")
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
username: Optional[str] = Field(None, min_length=3, max_length=64)
|
||||
first_name: Optional[str] = Field(None, max_length=64)
|
||||
last_name: Optional[str] = Field(None, max_length=64)
|
||||
email: Optional[EmailStr] = Field(None)
|
||||
phone_number: Optional[str] = Field(None, min_length=2, max_length=16, pattern=r'^\+[1-9]\d{1,14}$')
|
||||
password: Optional[str] = Field(None, min_length=8, max_length=128, examples=["Abc123#!"],
|
||||
description="Password must conform to this regex: \n```\n^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$\n```")
|
||||
|
||||
class Token(SQLModel):
|
||||
@model_validator(mode="after")
|
||||
def validate_using_regex(self):
|
||||
self.__validate_password()
|
||||
return self
|
||||
|
||||
def __validate_password(self):
|
||||
password_regex = r"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$"
|
||||
if not re.match(password_regex, self.password):
|
||||
raise ValueError("Password is too weak")
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
|
||||
|
||||
class TokenPayload(SQLModel):
|
||||
class TokenPayload(BaseModel):
|
||||
sub: str | None = None
|
||||
|
@ -1,27 +1,19 @@
|
||||
import logging
|
||||
from functools import wraps
|
||||
|
||||
from fastapi import HTTPException
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from sqlalchemy.exc import DatabaseError as SqlAlchemyDatabaseError
|
||||
|
||||
from app.core.errors import RepositoryError, ServiceError
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def propagate_db_error_to_service(func):
|
||||
def raise_http_errors(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except (SqlAlchemyDatabaseError, RepositoryError) as e:
|
||||
raise ServiceError(str(e)) from e
|
||||
return wrapper
|
||||
|
||||
|
||||
def propagate_service_errors_to_http_errors(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except ServiceError as e:
|
||||
raise HTTPException(status_code=500, detail=str(e)) from e
|
||||
except Exception as e:
|
||||
if isinstance(e, HTTPException):
|
||||
raise
|
||||
logger.error(e)
|
||||
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error")
|
||||
return wrapper
|
||||
|
@ -32,6 +32,7 @@ ignore = [
|
||||
"B008", # do not perform function calls in argument defaults
|
||||
"W191", # indentation contains tabs
|
||||
"B904", # Allow raising exceptions without from e, for HTTPException
|
||||
"UP007",
|
||||
]
|
||||
|
||||
[tool.autopep8]
|
||||
|
@ -1,18 +0,0 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import type { ClientOptions } from './types.gen';
|
||||
import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from '@hey-api/client-axios';
|
||||
|
||||
/**
|
||||
* The `createClientConfig()` function will be called on client initialization
|
||||
* and the returned object will become the client's initial configuration.
|
||||
*
|
||||
* You may want to initialize your client this way instead of calling
|
||||
* `setConfig()`. This is useful for example if you're using Next.js
|
||||
* to ensure your client always has the correct values.
|
||||
*/
|
||||
export type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> = (override?: Config<DefaultClientOptions & T>) => Config<Required<DefaultClientOptions> & T>;
|
||||
|
||||
export const client = createClient(createConfig<ClientOptions>({
|
||||
baseURL: 'http://localhost:8000'
|
||||
}));
|
@ -1,3 +0,0 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
export * from './types.gen';
|
||||
export * from './sdk.gen';
|
@ -1,217 +0,0 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import { type Options as ClientOptions, type TDataShape, type Client, urlSearchParamsBodySerializer } from '@hey-api/client-axios';
|
||||
import type { CartShowCartData, CartAddToCartData, CartAddToCartError, CartRemoveFromCartData, CartRemoveFromCartError, CartUpdateCountInCartData, CartUpdateCountInCartError, CartPurchaseData, UserDeleteUserData, UserLogoutData, UserRegisterData, UserRegisterError, UserUpdateUserData, UserUpdateUserError, UtilsHealthCheckData, UtilsHealthCheckResponse, UtilsTestDbData, UtilsTestDbResponse, LoginLoginAccessTokenData, LoginLoginAccessTokenResponse, LoginLoginAccessTokenError, ShopLoginAccessTokenData, ShopLoginAccessTokenResponse, ShopLoginAccessTokenError, ShopDeleteUserData, ShopDeleteUserError, ShopLogoutData, ShopRegisterData, ShopRegisterError, ShopUpdateUserData, ShopUpdateUserError } from './types.gen';
|
||||
import { client as _heyApiClient } from './client.gen';
|
||||
|
||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
|
||||
/**
|
||||
* You can provide a client instance returned by `createClient()` instead of
|
||||
* individual options. This might be also useful if you want to implement a
|
||||
* custom client.
|
||||
*/
|
||||
client?: Client;
|
||||
/**
|
||||
* You can pass arbitrary values through the `meta` object. This can be
|
||||
* used to access values that aren't defined as part of the SDK function.
|
||||
*/
|
||||
meta?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Show Cart
|
||||
*/
|
||||
export const cartShowCart = <ThrowOnError extends boolean = false>(options?: Options<CartShowCartData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).get<unknown, unknown, ThrowOnError>({
|
||||
url: '/cart/',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add To Cart
|
||||
*/
|
||||
export const cartAddToCart = <ThrowOnError extends boolean = false>(options: Options<CartAddToCartData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).put<unknown, CartAddToCartError, ThrowOnError>({
|
||||
url: '/cart/add/{product_id}',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove From Cart
|
||||
*/
|
||||
export const cartRemoveFromCart = <ThrowOnError extends boolean = false>(options: Options<CartRemoveFromCartData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).delete<unknown, CartRemoveFromCartError, ThrowOnError>({
|
||||
url: '/cart/remove/{product_id}',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update Count In Cart
|
||||
*/
|
||||
export const cartUpdateCountInCart = <ThrowOnError extends boolean = false>(options: Options<CartUpdateCountInCartData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).put<unknown, CartUpdateCountInCartError, ThrowOnError>({
|
||||
url: '/cart/update/{product_id}',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Purchase
|
||||
*/
|
||||
export const cartPurchase = <ThrowOnError extends boolean = false>(options?: Options<CartPurchaseData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).get<unknown, unknown, ThrowOnError>({
|
||||
url: '/cart/purchase',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete user
|
||||
*/
|
||||
export const userDeleteUser = <ThrowOnError extends boolean = false>(options?: Options<UserDeleteUserData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).delete<unknown, unknown, ThrowOnError>({
|
||||
url: '/user/delete',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* User logout
|
||||
*/
|
||||
export const userLogout = <ThrowOnError extends boolean = false>(options?: Options<UserLogoutData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).delete<unknown, unknown, ThrowOnError>({
|
||||
url: '/user/logout',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Register new user
|
||||
*/
|
||||
export const userRegister = <ThrowOnError extends boolean = false>(options: Options<UserRegisterData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).post<unknown, UserRegisterError, ThrowOnError>({
|
||||
url: '/user/register',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update user details
|
||||
*/
|
||||
export const userUpdateUser = <ThrowOnError extends boolean = false>(options: Options<UserUpdateUserData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).put<unknown, UserUpdateUserError, ThrowOnError>({
|
||||
url: '/user/update',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Health Check
|
||||
*/
|
||||
export const utilsHealthCheck = <ThrowOnError extends boolean = false>(options?: Options<UtilsHealthCheckData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).get<UtilsHealthCheckResponse, unknown, ThrowOnError>({
|
||||
url: '/utils/health-check/',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Test Db
|
||||
*/
|
||||
export const utilsTestDb = <ThrowOnError extends boolean = false>(options?: Options<UtilsTestDbData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).get<UtilsTestDbResponse, unknown, ThrowOnError>({
|
||||
url: '/utils/test-db/',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Login Access Token
|
||||
* OAuth2 compatible token login, get an access token for future requests
|
||||
*/
|
||||
export const loginLoginAccessToken = <ThrowOnError extends boolean = false>(options: Options<LoginLoginAccessTokenData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).post<LoginLoginAccessTokenResponse, LoginLoginAccessTokenError, ThrowOnError>({
|
||||
...urlSearchParamsBodySerializer,
|
||||
url: '/login/access-token',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
...options?.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Login Access Token
|
||||
* OAuth2 compatible token login, get an access token for future requests
|
||||
*/
|
||||
export const shopLoginAccessToken = <ThrowOnError extends boolean = false>(options: Options<ShopLoginAccessTokenData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).post<ShopLoginAccessTokenResponse, ShopLoginAccessTokenError, ThrowOnError>({
|
||||
...urlSearchParamsBodySerializer,
|
||||
url: '/shop/{shop_uuid}/login/access-token',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
...options?.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete user
|
||||
*/
|
||||
export const shopDeleteUser = <ThrowOnError extends boolean = false>(options: Options<ShopDeleteUserData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).delete<unknown, ShopDeleteUserError, ThrowOnError>({
|
||||
url: '/shop/{shop_uuid}/user/delete',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* User logout
|
||||
*/
|
||||
export const shopLogout = <ThrowOnError extends boolean = false>(options?: Options<ShopLogoutData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).delete<unknown, unknown, ThrowOnError>({
|
||||
url: '/shop/{shop_uuid}/user/logout',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Register new user
|
||||
*/
|
||||
export const shopRegister = <ThrowOnError extends boolean = false>(options: Options<ShopRegisterData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).post<unknown, ShopRegisterError, ThrowOnError>({
|
||||
url: '/shop/{shop_uuid}/user/register',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update user details
|
||||
*/
|
||||
export const shopUpdateUser = <ThrowOnError extends boolean = false>(options: Options<ShopUpdateUserData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).put<unknown, ShopUpdateUserError, ThrowOnError>({
|
||||
url: '/shop/{shop_uuid}/user/update',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
}
|
||||
});
|
||||
};
|
@ -1,408 +0,0 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
export type BodyLoginLoginAccessToken = {
|
||||
grant_type?: string | null;
|
||||
username: string;
|
||||
password: string;
|
||||
scope?: string;
|
||||
client_id?: string | null;
|
||||
client_secret?: string | null;
|
||||
};
|
||||
|
||||
export type BodyShopLoginAccessToken = {
|
||||
grant_type?: string | null;
|
||||
username: string;
|
||||
password: string;
|
||||
scope?: string;
|
||||
client_id?: string | null;
|
||||
client_secret?: string | null;
|
||||
};
|
||||
|
||||
export type HttpValidationError = {
|
||||
detail?: Array<ValidationError>;
|
||||
};
|
||||
|
||||
export type Token = {
|
||||
access_token: string;
|
||||
token_type?: string;
|
||||
};
|
||||
|
||||
export type UserRegister = {
|
||||
username: string;
|
||||
email: string;
|
||||
phone_number: string;
|
||||
/**
|
||||
* Password must be at least 8 and at most 128 characters long, contain a lower case, upper case letter, a number and a special character (#?!@$ %^&*-)
|
||||
*/
|
||||
password: string;
|
||||
};
|
||||
|
||||
export type ValidationError = {
|
||||
loc: Array<string | number>;
|
||||
msg: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type CartShowCartData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/cart/';
|
||||
};
|
||||
|
||||
export type CartShowCartResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type CartAddToCartData = {
|
||||
body?: never;
|
||||
path: {
|
||||
product_id: number;
|
||||
};
|
||||
query?: {
|
||||
/**
|
||||
* Count must be greater than or equal to 1
|
||||
*/
|
||||
count?: number;
|
||||
};
|
||||
url: '/cart/add/{product_id}';
|
||||
};
|
||||
|
||||
export type CartAddToCartErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type CartAddToCartError = CartAddToCartErrors[keyof CartAddToCartErrors];
|
||||
|
||||
export type CartAddToCartResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type CartRemoveFromCartData = {
|
||||
body?: never;
|
||||
path: {
|
||||
product_id: number;
|
||||
};
|
||||
query?: never;
|
||||
url: '/cart/remove/{product_id}';
|
||||
};
|
||||
|
||||
export type CartRemoveFromCartErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type CartRemoveFromCartError = CartRemoveFromCartErrors[keyof CartRemoveFromCartErrors];
|
||||
|
||||
export type CartRemoveFromCartResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type CartUpdateCountInCartData = {
|
||||
body?: never;
|
||||
path: {
|
||||
product_id: number;
|
||||
};
|
||||
query: {
|
||||
/**
|
||||
* Count must be provided
|
||||
*/
|
||||
count: number;
|
||||
};
|
||||
url: '/cart/update/{product_id}';
|
||||
};
|
||||
|
||||
export type CartUpdateCountInCartErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type CartUpdateCountInCartError = CartUpdateCountInCartErrors[keyof CartUpdateCountInCartErrors];
|
||||
|
||||
export type CartUpdateCountInCartResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type CartPurchaseData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/cart/purchase';
|
||||
};
|
||||
|
||||
export type CartPurchaseResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type UserDeleteUserData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/user/delete';
|
||||
};
|
||||
|
||||
export type UserDeleteUserResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type UserLogoutData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/user/logout';
|
||||
};
|
||||
|
||||
export type UserLogoutResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type UserRegisterData = {
|
||||
body: UserRegister;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/user/register';
|
||||
};
|
||||
|
||||
export type UserRegisterErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type UserRegisterError = UserRegisterErrors[keyof UserRegisterErrors];
|
||||
|
||||
export type UserRegisterResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type UserUpdateUserData = {
|
||||
body: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/user/update';
|
||||
};
|
||||
|
||||
export type UserUpdateUserErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type UserUpdateUserError = UserUpdateUserErrors[keyof UserUpdateUserErrors];
|
||||
|
||||
export type UserUpdateUserResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type UtilsHealthCheckData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/utils/health-check/';
|
||||
};
|
||||
|
||||
export type UtilsHealthCheckResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: boolean;
|
||||
};
|
||||
|
||||
export type UtilsHealthCheckResponse = UtilsHealthCheckResponses[keyof UtilsHealthCheckResponses];
|
||||
|
||||
export type UtilsTestDbData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/utils/test-db/';
|
||||
};
|
||||
|
||||
export type UtilsTestDbResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: boolean;
|
||||
};
|
||||
|
||||
export type UtilsTestDbResponse = UtilsTestDbResponses[keyof UtilsTestDbResponses];
|
||||
|
||||
export type LoginLoginAccessTokenData = {
|
||||
body: BodyLoginLoginAccessToken;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/login/access-token';
|
||||
};
|
||||
|
||||
export type LoginLoginAccessTokenErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type LoginLoginAccessTokenError = LoginLoginAccessTokenErrors[keyof LoginLoginAccessTokenErrors];
|
||||
|
||||
export type LoginLoginAccessTokenResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: Token;
|
||||
};
|
||||
|
||||
export type LoginLoginAccessTokenResponse = LoginLoginAccessTokenResponses[keyof LoginLoginAccessTokenResponses];
|
||||
|
||||
export type ShopLoginAccessTokenData = {
|
||||
body: BodyShopLoginAccessToken;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/shop/{shop_uuid}/login/access-token';
|
||||
};
|
||||
|
||||
export type ShopLoginAccessTokenErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type ShopLoginAccessTokenError = ShopLoginAccessTokenErrors[keyof ShopLoginAccessTokenErrors];
|
||||
|
||||
export type ShopLoginAccessTokenResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: Token;
|
||||
};
|
||||
|
||||
export type ShopLoginAccessTokenResponse = ShopLoginAccessTokenResponses[keyof ShopLoginAccessTokenResponses];
|
||||
|
||||
export type ShopDeleteUserData = {
|
||||
body?: never;
|
||||
path: {
|
||||
shop_uuid: unknown;
|
||||
};
|
||||
query?: never;
|
||||
url: '/shop/{shop_uuid}/user/delete';
|
||||
};
|
||||
|
||||
export type ShopDeleteUserErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type ShopDeleteUserError = ShopDeleteUserErrors[keyof ShopDeleteUserErrors];
|
||||
|
||||
export type ShopDeleteUserResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type ShopLogoutData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/shop/{shop_uuid}/user/logout';
|
||||
};
|
||||
|
||||
export type ShopLogoutResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type ShopRegisterData = {
|
||||
body: UserRegister;
|
||||
path: {
|
||||
shop_uuid: unknown;
|
||||
};
|
||||
query?: never;
|
||||
url: '/shop/{shop_uuid}/user/register';
|
||||
};
|
||||
|
||||
export type ShopRegisterErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type ShopRegisterError = ShopRegisterErrors[keyof ShopRegisterErrors];
|
||||
|
||||
export type ShopRegisterResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type ShopUpdateUserData = {
|
||||
body: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/shop/{shop_uuid}/user/update';
|
||||
};
|
||||
|
||||
export type ShopUpdateUserErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type ShopUpdateUserError = ShopUpdateUserErrors[keyof ShopUpdateUserErrors];
|
||||
|
||||
export type ShopUpdateUserResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type ClientOptions = {
|
||||
baseURL: 'http://localhost:8000' | (string & {});
|
||||
};
|
@ -56,7 +56,7 @@ const MainNavbar = () => {
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Button variant="secondary" className="w-full text-sm">
|
||||
Login
|
||||
Sign in
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
|
@ -2,15 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import { useNavigate } from "@tanstack/react-router"
|
||||
import { useState } from "react"
|
||||
|
||||
import {
|
||||
type BodyLoginLoginAccessToken as AccessToken,
|
||||
type ApiError,
|
||||
LoginService,
|
||||
type UserPublic,
|
||||
type UserRegister,
|
||||
UsersService,
|
||||
} from "@/client"
|
||||
import { handleError } from "@/utils"
|
||||
import { handleServerError } from "@/utils/handle-server-error"
|
||||
|
||||
const isLoggedIn = () => {
|
||||
return localStorage.getItem("access_token") !== null
|
||||
@ -31,10 +23,10 @@ const useAuth = () => {
|
||||
UsersService.registerUser({ requestBody: data }),
|
||||
|
||||
onSuccess: () => {
|
||||
navigate({ to: "/login" })
|
||||
navigate({ to: "/sign-in" })
|
||||
},
|
||||
onError: (err: ApiError) => {
|
||||
handleError(err)
|
||||
handleServerError(err)
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] })
|
||||
@ -60,7 +52,7 @@ const useAuth = () => {
|
||||
|
||||
const logout = () => {
|
||||
localStorage.removeItem("access_token")
|
||||
navigate({ to: "/login" })
|
||||
navigate({ to: "/sign-in" })
|
||||
}
|
||||
|
||||
return {
|
||||
|
Loading…
x
Reference in New Issue
Block a user