diff --git a/frontend/src/client/@tanstack/react-query.gen.ts b/frontend/src/client/@tanstack/react-query.gen.ts index d928312..6de7907 100644 --- a/frontend/src/client/@tanstack/react-query.gen.ts +++ b/frontend/src/client/@tanstack/react-query.gen.ts @@ -22,6 +22,8 @@ import { getEvent, updateEvent, getEventBySlug, + generatePresignedUrlApiV1UploadsPresignedUrlPost, + uploadFileApiV1UploadsTokenPost, } from "../sdk.gen"; import { queryOptions, type UseMutationOptions } from "@tanstack/react-query"; import type { @@ -65,6 +67,11 @@ import type { UpdateEventError, UpdateEventResponse, GetEventBySlugData, + GeneratePresignedUrlApiV1UploadsPresignedUrlPostData, + GeneratePresignedUrlApiV1UploadsPresignedUrlPostError, + GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse, + UploadFileApiV1UploadsTokenPostData, + UploadFileApiV1UploadsTokenPostError, } from "../types.gen"; import type { AxiosError } from "axios"; import { client as _heyApiClient } from "../client.gen"; @@ -621,3 +628,88 @@ export const getEventBySlugOptions = (options: Options) => { queryKey: getEventBySlugQueryKey(options), }); }; + +export const generatePresignedUrlApiV1UploadsPresignedUrlPostQueryKey = ( + options: Options, +) => + createQueryKey("generatePresignedUrlApiV1UploadsPresignedUrlPost", options); + +export const generatePresignedUrlApiV1UploadsPresignedUrlPostOptions = ( + options: Options, +) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await generatePresignedUrlApiV1UploadsPresignedUrlPost({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }); + return data; + }, + queryKey: generatePresignedUrlApiV1UploadsPresignedUrlPostQueryKey(options), + }); +}; + +export const generatePresignedUrlApiV1UploadsPresignedUrlPostMutation = ( + options?: Partial< + Options + >, +) => { + const mutationOptions: UseMutationOptions< + GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse, + AxiosError, + Options + > = { + mutationFn: async (localOptions) => { + const { data } = await generatePresignedUrlApiV1UploadsPresignedUrlPost({ + ...options, + ...localOptions, + throwOnError: true, + }); + return data; + }, + }; + return mutationOptions; +}; + +export const uploadFileApiV1UploadsTokenPostQueryKey = ( + options: Options, +) => createQueryKey("uploadFileApiV1UploadsTokenPost", options); + +export const uploadFileApiV1UploadsTokenPostOptions = ( + options: Options, +) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await uploadFileApiV1UploadsTokenPost({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }); + return data; + }, + queryKey: uploadFileApiV1UploadsTokenPostQueryKey(options), + }); +}; + +export const uploadFileApiV1UploadsTokenPostMutation = ( + options?: Partial>, +) => { + const mutationOptions: UseMutationOptions< + unknown, + AxiosError, + Options + > = { + mutationFn: async (localOptions) => { + const { data } = await uploadFileApiV1UploadsTokenPost({ + ...options, + ...localOptions, + throwOnError: true, + }); + return data; + }, + }; + return mutationOptions; +}; diff --git a/frontend/src/client/schemas.gen.ts b/frontend/src/client/schemas.gen.ts index bd9b318..e2963e8 100644 --- a/frontend/src/client/schemas.gen.ts +++ b/frontend/src/client/schemas.gen.ts @@ -71,6 +71,19 @@ export const Body_login_oauthSchema = { title: "Body_login_oauth", } as const; +export const Body_upload_file_api_v1_uploads__token__postSchema = { + properties: { + file: { + type: "string", + format: "binary", + title: "File", + }, + }, + type: "object", + required: ["file"], + title: "Body_upload_file_api_v1_uploads__token__post", +} as const; + export const EventCreateSchema = { properties: { title: { @@ -1118,6 +1131,55 @@ export const PaginatedResponse_EventResponse_Schema = { title: "PaginatedResponse[EventResponse]", } as const; +export const PresignedUrlRequestSchema = { + properties: { + filename: { + type: "string", + title: "Filename", + description: "Original filename of the image", + }, + content_type: { + type: "string", + title: "Content Type", + description: "Content type of the file (e.g., image/jpeg)", + }, + folder: { + type: "string", + title: "Folder", + description: "Folder to store the file in", + default: "images", + }, + }, + type: "object", + required: ["filename", "content_type"], + title: "PresignedUrlRequest", + description: "Request model for generating presigned URLs.", +} as const; + +export const PresignedUrlResponseSchema = { + properties: { + upload_url: { + type: "string", + title: "Upload Url", + description: "URL to upload the file to", + }, + file_url: { + type: "string", + title: "File Url", + description: "URL where the file will be accessible after upload", + }, + expires_in: { + type: "integer", + title: "Expires In", + description: "Time in seconds until the upload URL expires", + }, + }, + type: "object", + required: ["upload_url", "file_url", "expires_in"], + title: "PresignedUrlResponse", + description: "Response model for presigned URL generation.", +} as const; + export const RefreshTokenRequestSchema = { properties: { refresh_token: { diff --git a/frontend/src/client/sdk.gen.ts b/frontend/src/client/sdk.gen.ts index a2cfd3b..0a221da 100644 --- a/frontend/src/client/sdk.gen.ts +++ b/frontend/src/client/sdk.gen.ts @@ -5,6 +5,7 @@ import { type TDataShape, type Client, urlSearchParamsBodySerializer, + formDataBodySerializer, } from "@hey-api/client-axios"; import type { RootGetData, @@ -63,6 +64,11 @@ import type { GetEventBySlugData, GetEventBySlugResponse, GetEventBySlugError, + GeneratePresignedUrlApiV1UploadsPresignedUrlPostData, + GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse, + GeneratePresignedUrlApiV1UploadsPresignedUrlPostError, + UploadFileApiV1UploadsTokenPostData, + UploadFileApiV1UploadsTokenPostError, } from "./types.gen"; import { client as _heyApiClient } from "./client.gen"; @@ -270,7 +276,6 @@ export const listEventThemes = ( /** * Create Theme - * Create new event theme. */ export const createEventTheme = ( options: Options, @@ -280,6 +285,12 @@ export const createEventTheme = ( CreateEventThemeError, ThrowOnError >({ + security: [ + { + scheme: "bearer", + type: "http", + }, + ], url: "/api/v1/event_themes/", ...options, headers: { @@ -331,7 +342,6 @@ export const getEventTheme = ( /** * Update Theme - * Update specific theme by ID. */ export const updateEventTheme = ( options: Options, @@ -341,6 +351,12 @@ export const updateEventTheme = ( UpdateEventThemeError, ThrowOnError >({ + security: [ + { + scheme: "bearer", + type: "http", + }, + ], url: "/api/v1/event_themes/{theme_id}", ...options, headers: { @@ -535,3 +551,65 @@ export const getEventBySlug = ( ...options, }); }; + +/** + * Generate Presigned Url + * Generate a presigned URL for uploading a file. + * + * This endpoint creates a secure token that allows direct upload to the storage system. + * After successful upload, the file will be accessible at the returned file_url. + */ +export const generatePresignedUrlApiV1UploadsPresignedUrlPost = < + ThrowOnError extends boolean = false, +>( + options: Options< + GeneratePresignedUrlApiV1UploadsPresignedUrlPostData, + ThrowOnError + >, +) => { + return (options.client ?? _heyApiClient).post< + GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse, + GeneratePresignedUrlApiV1UploadsPresignedUrlPostError, + ThrowOnError + >({ + security: [ + { + scheme: "bearer", + type: "http", + }, + ], + url: "/api/v1/uploads/presigned-url", + ...options, + headers: { + "Content-Type": "application/json", + ...options?.headers, + }, + }); +}; + +/** + * Upload File + * Upload a file using a presigned URL token. + * + * This endpoint handles the actual file upload after a presigned URL is generated. + * The token validates the upload permissions and destination. + */ +export const uploadFileApiV1UploadsTokenPost = < + ThrowOnError extends boolean = false, +>( + options: Options, +) => { + return (options.client ?? _heyApiClient).post< + unknown, + UploadFileApiV1UploadsTokenPostError, + ThrowOnError + >({ + ...formDataBodySerializer, + url: "/api/v1/uploads/{token}", + ...options, + headers: { + "Content-Type": null, + ...options?.headers, + }, + }); +}; diff --git a/frontend/src/client/types.gen.ts b/frontend/src/client/types.gen.ts index 8de963c..551d7af 100644 --- a/frontend/src/client/types.gen.ts +++ b/frontend/src/client/types.gen.ts @@ -14,6 +14,10 @@ export type BodyLoginOauth = { client_secret?: string | null; }; +export type BodyUploadFileApiV1UploadsTokenPost = { + file: Blob | File; +}; + export type EventCreate = { title: string; description?: string | null; @@ -179,6 +183,42 @@ export type PaginatedResponseEventResponse = { size: number; }; +/** + * Request model for generating presigned URLs. + */ +export type PresignedUrlRequest = { + /** + * Original filename of the image + */ + filename: string; + /** + * Content type of the file (e.g., image/jpeg) + */ + content_type: string; + /** + * Folder to store the file in + */ + folder?: string; +}; + +/** + * Response model for presigned URL generation. + */ +export type PresignedUrlResponse = { + /** + * URL to upload the file to + */ + upload_url: string; + /** + * URL where the file will be accessible after upload + */ + file_url: string; + /** + * Time in seconds until the upload URL expires + */ + expires_in: number; +}; + export type RefreshTokenRequest = { refresh_token: string; }; @@ -756,6 +796,59 @@ export type GetEventBySlugResponses = { export type GetEventBySlugResponse = GetEventBySlugResponses[keyof GetEventBySlugResponses]; +export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostData = { + body: PresignedUrlRequest; + path?: never; + query?: never; + url: "/api/v1/uploads/presigned-url"; +}; + +export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostError = + GeneratePresignedUrlApiV1UploadsPresignedUrlPostErrors[keyof GeneratePresignedUrlApiV1UploadsPresignedUrlPostErrors]; + +export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponses = { + /** + * Successful Response + */ + 200: PresignedUrlResponse; +}; + +export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse = + GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponses[keyof GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponses]; + +export type UploadFileApiV1UploadsTokenPostData = { + body: BodyUploadFileApiV1UploadsTokenPost; + path: { + token: string; + }; + query?: never; + url: "/api/v1/uploads/{token}"; +}; + +export type UploadFileApiV1UploadsTokenPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UploadFileApiV1UploadsTokenPostError = + UploadFileApiV1UploadsTokenPostErrors[keyof UploadFileApiV1UploadsTokenPostErrors]; + +export type UploadFileApiV1UploadsTokenPostResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + export type ClientOptions = { baseURL: "http://localhost:8000" | (string & {}); };