first commit
This commit is contained in:
25
frontend/src/client/core/ApiError.ts
Normal file
25
frontend/src/client/core/ApiError.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { ApiRequestOptions } from "./ApiRequestOptions"
|
||||
import type { ApiResult } from "./ApiResult"
|
||||
|
||||
export class ApiError extends Error {
|
||||
public readonly url: string
|
||||
public readonly status: number
|
||||
public readonly statusText: string
|
||||
public readonly body: unknown
|
||||
public readonly request: ApiRequestOptions
|
||||
|
||||
constructor(
|
||||
request: ApiRequestOptions,
|
||||
response: ApiResult,
|
||||
message: string,
|
||||
) {
|
||||
super(message)
|
||||
|
||||
this.name = "ApiError"
|
||||
this.url = response.url
|
||||
this.status = response.status
|
||||
this.statusText = response.statusText
|
||||
this.body = response.body
|
||||
this.request = request
|
||||
}
|
||||
}
|
20
frontend/src/client/core/ApiRequestOptions.ts
Normal file
20
frontend/src/client/core/ApiRequestOptions.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export type ApiRequestOptions = {
|
||||
readonly method:
|
||||
| "GET"
|
||||
| "PUT"
|
||||
| "POST"
|
||||
| "DELETE"
|
||||
| "OPTIONS"
|
||||
| "HEAD"
|
||||
| "PATCH"
|
||||
readonly url: string
|
||||
readonly path?: Record<string, unknown>
|
||||
readonly cookies?: Record<string, unknown>
|
||||
readonly headers?: Record<string, unknown>
|
||||
readonly query?: Record<string, unknown>
|
||||
readonly formData?: Record<string, unknown>
|
||||
readonly body?: any
|
||||
readonly mediaType?: string
|
||||
readonly responseHeader?: string
|
||||
readonly errors?: Record<number, string>
|
||||
}
|
7
frontend/src/client/core/ApiResult.ts
Normal file
7
frontend/src/client/core/ApiResult.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type ApiResult<TData = any> = {
|
||||
readonly body: TData
|
||||
readonly ok: boolean
|
||||
readonly status: number
|
||||
readonly statusText: string
|
||||
readonly url: string
|
||||
}
|
126
frontend/src/client/core/CancelablePromise.ts
Normal file
126
frontend/src/client/core/CancelablePromise.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
export class CancelError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message)
|
||||
this.name = "CancelError"
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export interface OnCancel {
|
||||
readonly isResolved: boolean
|
||||
readonly isRejected: boolean
|
||||
readonly isCancelled: boolean
|
||||
|
||||
(cancelHandler: () => void): void
|
||||
}
|
||||
|
||||
export class CancelablePromise<T> implements Promise<T> {
|
||||
private _isResolved: boolean
|
||||
private _isRejected: boolean
|
||||
private _isCancelled: boolean
|
||||
readonly cancelHandlers: (() => void)[]
|
||||
readonly promise: Promise<T>
|
||||
private _resolve?: (value: T | PromiseLike<T>) => void
|
||||
private _reject?: (reason?: unknown) => void
|
||||
|
||||
constructor(
|
||||
executor: (
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason?: unknown) => void,
|
||||
onCancel: OnCancel,
|
||||
) => void,
|
||||
) {
|
||||
this._isResolved = false
|
||||
this._isRejected = false
|
||||
this._isCancelled = false
|
||||
this.cancelHandlers = []
|
||||
this.promise = new Promise<T>((resolve, reject) => {
|
||||
this._resolve = resolve
|
||||
this._reject = reject
|
||||
|
||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return
|
||||
}
|
||||
this._isResolved = true
|
||||
if (this._resolve) this._resolve(value)
|
||||
}
|
||||
|
||||
const onReject = (reason?: unknown): void => {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return
|
||||
}
|
||||
this._isRejected = true
|
||||
if (this._reject) this._reject(reason)
|
||||
}
|
||||
|
||||
const onCancel = (cancelHandler: () => void): void => {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return
|
||||
}
|
||||
this.cancelHandlers.push(cancelHandler)
|
||||
}
|
||||
|
||||
Object.defineProperty(onCancel, "isResolved", {
|
||||
get: (): boolean => this._isResolved,
|
||||
})
|
||||
|
||||
Object.defineProperty(onCancel, "isRejected", {
|
||||
get: (): boolean => this._isRejected,
|
||||
})
|
||||
|
||||
Object.defineProperty(onCancel, "isCancelled", {
|
||||
get: (): boolean => this._isCancelled,
|
||||
})
|
||||
|
||||
return executor(onResolve, onReject, onCancel as OnCancel)
|
||||
})
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return "Cancellable Promise"
|
||||
}
|
||||
|
||||
public then<TResult1 = T, TResult2 = never>(
|
||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
|
||||
): Promise<TResult1 | TResult2> {
|
||||
return this.promise.then(onFulfilled, onRejected)
|
||||
}
|
||||
|
||||
public catch<TResult = never>(
|
||||
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
|
||||
): Promise<T | TResult> {
|
||||
return this.promise.catch(onRejected)
|
||||
}
|
||||
|
||||
public finally(onFinally?: (() => void) | null): Promise<T> {
|
||||
return this.promise.finally(onFinally)
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return
|
||||
}
|
||||
this._isCancelled = true
|
||||
if (this.cancelHandlers.length) {
|
||||
try {
|
||||
for (const cancelHandler of this.cancelHandlers) {
|
||||
cancelHandler()
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Cancellation threw an error", error)
|
||||
return
|
||||
}
|
||||
}
|
||||
this.cancelHandlers.length = 0
|
||||
if (this._reject) this._reject(new CancelError("Request aborted"))
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return this._isCancelled
|
||||
}
|
||||
}
|
57
frontend/src/client/core/OpenAPI.ts
Normal file
57
frontend/src/client/core/OpenAPI.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { AxiosRequestConfig, AxiosResponse } from "axios"
|
||||
import type { ApiRequestOptions } from "./ApiRequestOptions"
|
||||
import type { TResult } from "./types"
|
||||
|
||||
type Headers = Record<string, string>
|
||||
type Middleware<T> = (value: T) => T | Promise<T>
|
||||
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>
|
||||
|
||||
export class Interceptors<T> {
|
||||
_fns: Middleware<T>[]
|
||||
|
||||
constructor() {
|
||||
this._fns = []
|
||||
}
|
||||
|
||||
eject(fn: Middleware<T>) {
|
||||
const index = this._fns.indexOf(fn)
|
||||
if (index !== -1) {
|
||||
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]
|
||||
}
|
||||
}
|
||||
|
||||
use(fn: Middleware<T>) {
|
||||
this._fns = [...this._fns, fn]
|
||||
}
|
||||
}
|
||||
|
||||
export type OpenAPIConfig = {
|
||||
BASE: string
|
||||
CREDENTIALS: "include" | "omit" | "same-origin"
|
||||
ENCODE_PATH?: ((path: string) => string) | undefined
|
||||
HEADERS?: Headers | Resolver<Headers> | undefined
|
||||
PASSWORD?: string | Resolver<string> | undefined
|
||||
RESULT?: TResult
|
||||
TOKEN?: string | Resolver<string> | undefined
|
||||
USERNAME?: string | Resolver<string> | undefined
|
||||
VERSION: string
|
||||
WITH_CREDENTIALS: boolean
|
||||
interceptors: {
|
||||
request: Interceptors<AxiosRequestConfig>
|
||||
response: Interceptors<AxiosResponse>
|
||||
}
|
||||
}
|
||||
|
||||
export const OpenAPI: OpenAPIConfig = {
|
||||
BASE: "",
|
||||
CREDENTIALS: "include",
|
||||
ENCODE_PATH: undefined,
|
||||
HEADERS: undefined,
|
||||
PASSWORD: undefined,
|
||||
RESULT: "body",
|
||||
TOKEN: undefined,
|
||||
USERNAME: undefined,
|
||||
VERSION: "0.1.0",
|
||||
WITH_CREDENTIALS: false,
|
||||
interceptors: { request: new Interceptors(), response: new Interceptors() },
|
||||
}
|
376
frontend/src/client/core/request.ts
Normal file
376
frontend/src/client/core/request.ts
Normal file
@@ -0,0 +1,376 @@
|
||||
import axios from "axios"
|
||||
import type {
|
||||
AxiosError,
|
||||
AxiosInstance,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
} from "axios"
|
||||
|
||||
import { ApiError } from "./ApiError"
|
||||
import type { ApiRequestOptions } from "./ApiRequestOptions"
|
||||
import type { ApiResult } from "./ApiResult"
|
||||
import { CancelablePromise } from "./CancelablePromise"
|
||||
import type { OnCancel } from "./CancelablePromise"
|
||||
import type { OpenAPIConfig } from "./OpenAPI"
|
||||
|
||||
export const isString = (value: unknown): value is string => {
|
||||
return typeof value === "string"
|
||||
}
|
||||
|
||||
export const isStringWithValue = (value: unknown): value is string => {
|
||||
return isString(value) && value !== ""
|
||||
}
|
||||
|
||||
export const isBlob = (value: any): value is Blob => {
|
||||
return value instanceof Blob
|
||||
}
|
||||
|
||||
export const isFormData = (value: unknown): value is FormData => {
|
||||
return value instanceof FormData
|
||||
}
|
||||
|
||||
export const isSuccess = (status: number): boolean => {
|
||||
return status >= 200 && status < 300
|
||||
}
|
||||
|
||||
export const base64 = (str: string): string => {
|
||||
try {
|
||||
return btoa(str)
|
||||
} catch (err) {
|
||||
// @ts-ignore
|
||||
return Buffer.from(str).toString("base64")
|
||||
}
|
||||
}
|
||||
|
||||
export const getQueryString = (params: Record<string, unknown>): string => {
|
||||
const qs: string[] = []
|
||||
|
||||
const append = (key: string, value: unknown) => {
|
||||
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
|
||||
}
|
||||
|
||||
const encodePair = (key: string, value: unknown) => {
|
||||
if (value === undefined || value === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((v) => encodePair(key, v))
|
||||
} else if (typeof value === "object") {
|
||||
Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v))
|
||||
} else {
|
||||
append(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => encodePair(key, value))
|
||||
|
||||
return qs.length ? `?${qs.join("&")}` : ""
|
||||
}
|
||||
|
||||
const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
|
||||
const encoder = config.ENCODE_PATH || encodeURI
|
||||
|
||||
const path = options.url
|
||||
.replace("{api-version}", config.VERSION)
|
||||
.replace(/{(.*?)}/g, (substring: string, group: string) => {
|
||||
if (options.path?.hasOwnProperty(group)) {
|
||||
return encoder(String(options.path[group]))
|
||||
}
|
||||
return substring
|
||||
})
|
||||
|
||||
const url = config.BASE + path
|
||||
return options.query ? url + getQueryString(options.query) : url
|
||||
}
|
||||
|
||||
export const getFormData = (
|
||||
options: ApiRequestOptions,
|
||||
): FormData | undefined => {
|
||||
if (options.formData) {
|
||||
const formData = new FormData()
|
||||
|
||||
const process = (key: string, value: unknown) => {
|
||||
if (isString(value) || isBlob(value)) {
|
||||
formData.append(key, value)
|
||||
} else {
|
||||
formData.append(key, JSON.stringify(value))
|
||||
}
|
||||
}
|
||||
|
||||
Object.entries(options.formData)
|
||||
.filter(([, value]) => value !== undefined && value !== null)
|
||||
.forEach(([key, value]) => {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((v) => process(key, v))
|
||||
} else {
|
||||
process(key, value)
|
||||
}
|
||||
})
|
||||
|
||||
return formData
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>
|
||||
|
||||
export const resolve = async <T>(
|
||||
options: ApiRequestOptions,
|
||||
resolver?: T | Resolver<T>,
|
||||
): Promise<T | undefined> => {
|
||||
if (typeof resolver === "function") {
|
||||
return (resolver as Resolver<T>)(options)
|
||||
}
|
||||
return resolver
|
||||
}
|
||||
|
||||
export const getHeaders = async (
|
||||
config: OpenAPIConfig,
|
||||
options: ApiRequestOptions,
|
||||
): Promise<Record<string, string>> => {
|
||||
const [token, username, password, additionalHeaders] = await Promise.all([
|
||||
resolve(options, config.TOKEN),
|
||||
resolve(options, config.USERNAME),
|
||||
resolve(options, config.PASSWORD),
|
||||
resolve(options, config.HEADERS),
|
||||
])
|
||||
|
||||
const headers = Object.entries({
|
||||
Accept: "application/json",
|
||||
...additionalHeaders,
|
||||
...options.headers,
|
||||
})
|
||||
.filter(([, value]) => value !== undefined && value !== null)
|
||||
.reduce(
|
||||
(headers, [key, value]) => ({
|
||||
...headers,
|
||||
[key]: String(value),
|
||||
}),
|
||||
{} as Record<string, string>,
|
||||
)
|
||||
|
||||
if (isStringWithValue(token)) {
|
||||
headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
|
||||
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||
const credentials = base64(`${username}:${password}`)
|
||||
headers.Authorization = `Basic ${credentials}`
|
||||
}
|
||||
|
||||
if (options.body !== undefined) {
|
||||
if (options.mediaType) {
|
||||
headers["Content-Type"] = options.mediaType
|
||||
} else if (isBlob(options.body)) {
|
||||
headers["Content-Type"] = options.body.type || "application/octet-stream"
|
||||
} else if (isString(options.body)) {
|
||||
headers["Content-Type"] = "text/plain"
|
||||
} else if (!isFormData(options.body)) {
|
||||
headers["Content-Type"] = "application/json"
|
||||
}
|
||||
} else if (options.formData !== undefined) {
|
||||
if (options.mediaType) {
|
||||
headers["Content-Type"] = options.mediaType
|
||||
}
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
export const getRequestBody = (options: ApiRequestOptions): unknown => {
|
||||
if (options.body) {
|
||||
return options.body
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const sendRequest = async <T>(
|
||||
config: OpenAPIConfig,
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
body: unknown,
|
||||
formData: FormData | undefined,
|
||||
headers: Record<string, string>,
|
||||
onCancel: OnCancel,
|
||||
axiosClient: AxiosInstance,
|
||||
): Promise<AxiosResponse<T>> => {
|
||||
const controller = new AbortController()
|
||||
|
||||
let requestConfig: AxiosRequestConfig = {
|
||||
data: body ?? formData,
|
||||
headers,
|
||||
method: options.method,
|
||||
signal: controller.signal,
|
||||
url,
|
||||
withCredentials: config.WITH_CREDENTIALS,
|
||||
}
|
||||
|
||||
onCancel(() => controller.abort())
|
||||
|
||||
for (const fn of config.interceptors.request._fns) {
|
||||
requestConfig = await fn(requestConfig)
|
||||
}
|
||||
|
||||
try {
|
||||
return await axiosClient.request(requestConfig)
|
||||
} catch (error) {
|
||||
const axiosError = error as AxiosError<T>
|
||||
if (axiosError.response) {
|
||||
return axiosError.response
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const getResponseHeader = (
|
||||
response: AxiosResponse<unknown>,
|
||||
responseHeader?: string,
|
||||
): string | undefined => {
|
||||
if (responseHeader) {
|
||||
const content = response.headers[responseHeader]
|
||||
if (isString(content)) {
|
||||
return content
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const getResponseBody = (response: AxiosResponse<unknown>): unknown => {
|
||||
if (response.status !== 204) {
|
||||
return response.data
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const catchErrorCodes = (
|
||||
options: ApiRequestOptions,
|
||||
result: ApiResult,
|
||||
): void => {
|
||||
const errors: Record<number, string> = {
|
||||
400: "Bad Request",
|
||||
401: "Unauthorized",
|
||||
402: "Payment Required",
|
||||
403: "Forbidden",
|
||||
404: "Not Found",
|
||||
405: "Method Not Allowed",
|
||||
406: "Not Acceptable",
|
||||
407: "Proxy Authentication Required",
|
||||
408: "Request Timeout",
|
||||
409: "Conflict",
|
||||
410: "Gone",
|
||||
411: "Length Required",
|
||||
412: "Precondition Failed",
|
||||
413: "Payload Too Large",
|
||||
414: "URI Too Long",
|
||||
415: "Unsupported Media Type",
|
||||
416: "Range Not Satisfiable",
|
||||
417: "Expectation Failed",
|
||||
418: "Im a teapot",
|
||||
421: "Misdirected Request",
|
||||
422: "Unprocessable Content",
|
||||
423: "Locked",
|
||||
424: "Failed Dependency",
|
||||
425: "Too Early",
|
||||
426: "Upgrade Required",
|
||||
428: "Precondition Required",
|
||||
429: "Too Many Requests",
|
||||
431: "Request Header Fields Too Large",
|
||||
451: "Unavailable For Legal Reasons",
|
||||
500: "Internal Server Error",
|
||||
501: "Not Implemented",
|
||||
502: "Bad Gateway",
|
||||
503: "Service Unavailable",
|
||||
504: "Gateway Timeout",
|
||||
505: "HTTP Version Not Supported",
|
||||
506: "Variant Also Negotiates",
|
||||
507: "Insufficient Storage",
|
||||
508: "Loop Detected",
|
||||
510: "Not Extended",
|
||||
511: "Network Authentication Required",
|
||||
...options.errors,
|
||||
}
|
||||
|
||||
const error = errors[result.status]
|
||||
if (error) {
|
||||
throw new ApiError(options, result, error)
|
||||
}
|
||||
|
||||
if (!result.ok) {
|
||||
const errorStatus = result.status ?? "unknown"
|
||||
const errorStatusText = result.statusText ?? "unknown"
|
||||
const errorBody = (() => {
|
||||
try {
|
||||
return JSON.stringify(result.body, null, 2)
|
||||
} catch (e) {
|
||||
return undefined
|
||||
}
|
||||
})()
|
||||
|
||||
throw new ApiError(
|
||||
options,
|
||||
result,
|
||||
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request method
|
||||
* @param config The OpenAPI configuration object
|
||||
* @param options The request options from the service
|
||||
* @param axiosClient The axios client instance to use
|
||||
* @returns CancelablePromise<T>
|
||||
* @throws ApiError
|
||||
*/
|
||||
export const request = <T>(
|
||||
config: OpenAPIConfig,
|
||||
options: ApiRequestOptions,
|
||||
axiosClient: AxiosInstance = axios,
|
||||
): CancelablePromise<T> => {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(config, options)
|
||||
const formData = getFormData(options)
|
||||
const body = getRequestBody(options)
|
||||
const headers = await getHeaders(config, options)
|
||||
|
||||
if (!onCancel.isCancelled) {
|
||||
let response = await sendRequest<T>(
|
||||
config,
|
||||
options,
|
||||
url,
|
||||
body,
|
||||
formData,
|
||||
headers,
|
||||
onCancel,
|
||||
axiosClient,
|
||||
)
|
||||
|
||||
for (const fn of config.interceptors.response._fns) {
|
||||
response = await fn(response)
|
||||
}
|
||||
|
||||
const responseBody = getResponseBody(response)
|
||||
const responseHeader = getResponseHeader(
|
||||
response,
|
||||
options.responseHeader,
|
||||
)
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: isSuccess(response.status),
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader ?? responseBody,
|
||||
}
|
||||
|
||||
catchErrorCodes(options, result)
|
||||
|
||||
resolve(result.body)
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
14
frontend/src/client/core/types.ts
Normal file
14
frontend/src/client/core/types.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { ApiResult } from "./ApiResult"
|
||||
|
||||
export type TResult = "body" | "raw"
|
||||
|
||||
export type TApiResponse<T extends TResult, TData> = Exclude<
|
||||
T,
|
||||
"raw"
|
||||
> extends never
|
||||
? ApiResult<TData>
|
||||
: ApiResult<TData>["body"]
|
||||
|
||||
export type TConfig<T extends TResult> = {
|
||||
_result?: T
|
||||
}
|
8
frontend/src/client/index.ts
Normal file
8
frontend/src/client/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export { ApiError } from "./core/ApiError"
|
||||
export { CancelablePromise, CancelError } from "./core/CancelablePromise"
|
||||
export { OpenAPI } from "./core/OpenAPI"
|
||||
export type { OpenAPIConfig } from "./core/OpenAPI"
|
||||
|
||||
export * from "./models"
|
||||
export * from "./schemas"
|
||||
export * from "./services"
|
284
frontend/src/client/models.ts
Normal file
284
frontend/src/client/models.ts
Normal file
@@ -0,0 +1,284 @@
|
||||
export type Body_login_login_access_token = {
|
||||
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 ItemCreate = {
|
||||
title: string
|
||||
description?: string | null
|
||||
}
|
||||
|
||||
export type ItemPublic = {
|
||||
title: string
|
||||
description?: string | null
|
||||
id: string
|
||||
owner_id: string
|
||||
}
|
||||
|
||||
export type ItemUpdate = {
|
||||
title?: string | null
|
||||
description?: string | null
|
||||
}
|
||||
|
||||
export type ItemsPublic = {
|
||||
data: Array<ItemPublic>
|
||||
count: number
|
||||
}
|
||||
|
||||
export type ClientMessagePublic = {
|
||||
name: string,
|
||||
phone: string,
|
||||
email: string,
|
||||
message: string,
|
||||
id: string,
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export type ClientMessagesPublic = {
|
||||
data: Array<ClientMessagePublic>,
|
||||
count: number
|
||||
}
|
||||
|
||||
export type Message = {
|
||||
message: string
|
||||
}
|
||||
|
||||
export type NewPassword = {
|
||||
token: string
|
||||
new_password: string
|
||||
}
|
||||
|
||||
export type Token = {
|
||||
access_token: string
|
||||
token_type?: string
|
||||
}
|
||||
|
||||
export type UpdatePassword = {
|
||||
current_password: string
|
||||
new_password: string
|
||||
}
|
||||
|
||||
export type UserCreate = {
|
||||
email: string
|
||||
is_active?: boolean
|
||||
is_superuser?: boolean
|
||||
full_name?: string | null
|
||||
password: string
|
||||
}
|
||||
|
||||
export type UserPublic = {
|
||||
email: string
|
||||
is_active?: boolean
|
||||
is_superuser?: boolean
|
||||
full_name?: string | null
|
||||
id: string
|
||||
}
|
||||
|
||||
export type UserRegister = {
|
||||
email: string
|
||||
password: string
|
||||
full_name?: string | null
|
||||
}
|
||||
|
||||
export type UserUpdate = {
|
||||
email?: string | null
|
||||
is_active?: boolean
|
||||
is_superuser?: boolean
|
||||
full_name?: string | null
|
||||
password?: string | null
|
||||
}
|
||||
|
||||
export type UserUpdateMe = {
|
||||
full_name?: string | null
|
||||
email?: string | null
|
||||
}
|
||||
|
||||
export type UsersPublic = {
|
||||
data: Array<UserPublic>
|
||||
count: number
|
||||
}
|
||||
|
||||
export type ValidationError = {
|
||||
loc: Array<string | number>
|
||||
msg: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export type WebSettingPublic = {
|
||||
address: string,
|
||||
google_map_api_key: string,
|
||||
latitude: Number,
|
||||
longitude: Number,
|
||||
phone: string,
|
||||
email: string,
|
||||
facebook: string,
|
||||
instagram: string,
|
||||
youtube: string,
|
||||
youtube_link: string,
|
||||
whatsapp: string,
|
||||
id: string
|
||||
}
|
||||
|
||||
export type WebSettingUpdate = {
|
||||
address: string,
|
||||
google_map_api_key: string,
|
||||
latitude: Number,
|
||||
longitude: Number,
|
||||
phone: string,
|
||||
email: string,
|
||||
facebook: string,
|
||||
instagram: string,
|
||||
youtube: string,
|
||||
youtube_link: string,
|
||||
whatsapp: string,
|
||||
}
|
||||
|
||||
|
||||
export type AboutUssPublic = {
|
||||
data: Array<AboutUsPublic>
|
||||
count: number
|
||||
}
|
||||
|
||||
export type AboutUsPublic = {
|
||||
index: number,
|
||||
description: string,
|
||||
image: string,
|
||||
id: string
|
||||
}
|
||||
|
||||
export type AboutUsCreate = {
|
||||
index: number,
|
||||
description: string,
|
||||
image: File,
|
||||
}
|
||||
|
||||
export type AboutUsUpdate = {
|
||||
index: number,
|
||||
description: string,
|
||||
image?: File | undefined | null,
|
||||
}
|
||||
|
||||
export type CoursesPublic = {
|
||||
data: Array<CoursePublic>
|
||||
count: number
|
||||
}
|
||||
|
||||
export type CoursePublic = {
|
||||
title: string,
|
||||
sort_description: string,
|
||||
long_description: string,
|
||||
information: string,
|
||||
contant: string,
|
||||
remark: string,
|
||||
id: string,
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export type CourseCreate = {
|
||||
title: string,
|
||||
sort_description: string,
|
||||
long_description: string,
|
||||
information: string,
|
||||
contant: string,
|
||||
remark: string,
|
||||
}
|
||||
|
||||
export type CourseDetailsPublic = {
|
||||
title: string,
|
||||
sort_description: string,
|
||||
long_description: string,
|
||||
information: string,
|
||||
contant: string,
|
||||
remark: string,
|
||||
id: string,
|
||||
created_at: string
|
||||
images: Array<ImagePublic>,
|
||||
info_images: Array<Info_imagePublic>,
|
||||
schedule: Array<SchedulePublic>
|
||||
}
|
||||
|
||||
export type CourseUpdate = {
|
||||
title: string,
|
||||
sort_description: string,
|
||||
long_description: string,
|
||||
information: string,
|
||||
contant: string,
|
||||
remark: string,
|
||||
}
|
||||
|
||||
export type ImagesPublic = {
|
||||
data: Array<ImagePublic>,
|
||||
}
|
||||
|
||||
export type ImagePublic = {
|
||||
image: string,
|
||||
course_id: string,
|
||||
index: number,
|
||||
id:string
|
||||
}
|
||||
|
||||
export type ImageUpdate = {
|
||||
index: number,
|
||||
}
|
||||
|
||||
export type ImageCreate = {
|
||||
image: File,
|
||||
index: number,
|
||||
course_id: string
|
||||
}
|
||||
|
||||
export type Info_imagesPublic = {
|
||||
data: Array<Info_imagePublic>,
|
||||
}
|
||||
|
||||
export type Info_imagePublic = {
|
||||
image: string,
|
||||
course_id: string,
|
||||
index: number
|
||||
id: string
|
||||
}
|
||||
|
||||
export type Info_imageUpdate = {
|
||||
index: number,
|
||||
}
|
||||
|
||||
export type Info_imagesCreate = {
|
||||
image: File,
|
||||
index: number,
|
||||
course_id: string
|
||||
}
|
||||
|
||||
export type SchedulesPublic = {
|
||||
data: Array<SchedulePublic>,
|
||||
}
|
||||
|
||||
export type SchedulePublic = {
|
||||
title: string,
|
||||
info1: string,
|
||||
info2: string,
|
||||
date: string,
|
||||
course_id: string,
|
||||
id:string
|
||||
}
|
||||
export type ScheduleCreate = {
|
||||
title: string,
|
||||
info1: string,
|
||||
info2: string,
|
||||
date: string,
|
||||
course_id: string,
|
||||
}
|
||||
|
||||
export type ScheduleUpdate = {
|
||||
title: string,
|
||||
info1: string,
|
||||
info2: string,
|
||||
date: string,
|
||||
}
|
444
frontend/src/client/schemas.ts
Normal file
444
frontend/src/client/schemas.ts
Normal file
@@ -0,0 +1,444 @@
|
||||
export const $Body_login_login_access_token = {
|
||||
properties: {
|
||||
grant_type: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
pattern: "password",
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
username: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
},
|
||||
password: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
},
|
||||
scope: {
|
||||
type: "string",
|
||||
default: "",
|
||||
},
|
||||
client_id: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
client_secret: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $HTTPValidationError = {
|
||||
properties: {
|
||||
detail: {
|
||||
type: "array",
|
||||
contains: {
|
||||
type: "ValidationError",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $ItemCreate = {
|
||||
properties: {
|
||||
title: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
maxLength: 255,
|
||||
minLength: 1,
|
||||
},
|
||||
description: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $ItemPublic = {
|
||||
properties: {
|
||||
title: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
maxLength: 255,
|
||||
minLength: 1,
|
||||
},
|
||||
description: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
id: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
format: "uuid",
|
||||
},
|
||||
owner_id: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
format: "uuid",
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $ItemUpdate = {
|
||||
properties: {
|
||||
title: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 255,
|
||||
minLength: 1,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
description: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $ItemsPublic = {
|
||||
properties: {
|
||||
data: {
|
||||
type: "array",
|
||||
contains: {
|
||||
type: "ItemPublic",
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
count: {
|
||||
type: "number",
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $Message = {
|
||||
properties: {
|
||||
message: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $NewPassword = {
|
||||
properties: {
|
||||
token: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
},
|
||||
new_password: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
maxLength: 40,
|
||||
minLength: 8,
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $Token = {
|
||||
properties: {
|
||||
access_token: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
},
|
||||
token_type: {
|
||||
type: "string",
|
||||
default: "bearer",
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $UpdatePassword = {
|
||||
properties: {
|
||||
current_password: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
maxLength: 40,
|
||||
minLength: 8,
|
||||
},
|
||||
new_password: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
maxLength: 40,
|
||||
minLength: 8,
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $UserCreate = {
|
||||
properties: {
|
||||
email: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
format: "email",
|
||||
maxLength: 255,
|
||||
},
|
||||
is_active: {
|
||||
type: "boolean",
|
||||
default: true,
|
||||
},
|
||||
is_superuser: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
full_name: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
password: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
maxLength: 40,
|
||||
minLength: 8,
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $UserPublic = {
|
||||
properties: {
|
||||
email: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
format: "email",
|
||||
maxLength: 255,
|
||||
},
|
||||
is_active: {
|
||||
type: "boolean",
|
||||
default: true,
|
||||
},
|
||||
is_superuser: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
full_name: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
id: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
format: "uuid",
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $UserRegister = {
|
||||
properties: {
|
||||
email: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
format: "email",
|
||||
maxLength: 255,
|
||||
},
|
||||
password: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
maxLength: 40,
|
||||
minLength: 8,
|
||||
},
|
||||
full_name: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $UserUpdate = {
|
||||
properties: {
|
||||
email: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
format: "email",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
is_active: {
|
||||
type: "boolean",
|
||||
default: true,
|
||||
},
|
||||
is_superuser: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
full_name: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
password: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 40,
|
||||
minLength: 8,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $UserUpdateMe = {
|
||||
properties: {
|
||||
full_name: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
email: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
format: "email",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $UsersPublic = {
|
||||
properties: {
|
||||
data: {
|
||||
type: "array",
|
||||
contains: {
|
||||
type: "UserPublic",
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
count: {
|
||||
type: "number",
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
export const $ValidationError = {
|
||||
properties: {
|
||||
loc: {
|
||||
type: "array",
|
||||
contains: {
|
||||
type: "any-of",
|
||||
contains: [
|
||||
{
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
type: "number",
|
||||
},
|
||||
],
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
msg: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: "string",
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const
|
1045
frontend/src/client/services.ts
Normal file
1045
frontend/src/client/services.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user