fix: adds missing storage api docs and fixes api key auth docs (#5031)

This commit is contained in:
Anshuman Pandey
2025-03-24 12:13:57 +05:30
committed by GitHub
parent 777210ec42
commit 60d0563487
4 changed files with 554 additions and 22 deletions

View File

@@ -36,13 +36,13 @@ export const POST = async (req: NextRequest, context: Context): Promise<Response
const accessType = "private"; // private files are accessible only by authorized users
const formData = await req.json();
const fileType = formData.fileType as string;
const encodedFileName = formData.fileName as string;
const surveyId = formData.surveyId as string;
const signedSignature = formData.signature as string;
const signedUuid = formData.uuid as string;
const signedTimestamp = formData.timestamp as string;
const jsonInput = await req.json();
const fileType = jsonInput.fileType as string;
const encodedFileName = jsonInput.fileName as string;
const surveyId = jsonInput.surveyId as string;
const signedSignature = jsonInput.signature as string;
const signedUuid = jsonInput.uuid as string;
const signedTimestamp = jsonInput.timestamp as string;
if (!fileType) {
return responses.badRequestResponse("contentType is required");
@@ -99,7 +99,7 @@ export const POST = async (req: NextRequest, context: Context): Promise<Response
return responses.unauthorizedResponse();
}
const base64String = formData.fileBase64String as string;
const base64String = jsonInput.fileBase64String as string;
const buffer = Buffer.from(base64String.split(",")[1], "base64");
const file = new Blob([buffer], { type: fileType });

View File

@@ -1,8 +1,10 @@
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { getBiggerUploadFileSizePermission } from "@/modules/ee/license-check/lib/utils";
import { NextRequest } from "next/server";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getSurvey } from "@formbricks/lib/survey/service";
import { ZUploadFileRequest } from "@formbricks/types/storage";
import { uploadPrivateFile } from "./lib/uploadPrivateFile";
interface Context {
@@ -25,19 +27,22 @@ export const POST = async (req: NextRequest, context: Context): Promise<Response
const params = await context.params;
const environmentId = params.environmentId;
const { fileName, fileType, surveyId } = await req.json();
const jsonInput = await req.json();
if (!surveyId) {
return responses.badRequestResponse("surveyId ID is required");
const inputValidation = ZUploadFileRequest.safeParse({
...jsonInput,
environmentId,
});
if (!inputValidation.success) {
return responses.badRequestResponse(
"Invalid request",
transformErrorToDetails(inputValidation.error),
true
);
}
if (!fileName) {
return responses.badRequestResponse("fileName is required");
}
if (!fileType) {
return responses.badRequestResponse("contentType is required");
}
const { fileName, fileType, surveyId } = inputValidation.data;
const [survey, organization] = await Promise.all([
getSurvey(surveyId),

View File

@@ -17,14 +17,22 @@ tags:
description: Operations for managing contact attributes keys.
- name: Management API > Surveys
description: Operations for managing surveys.
security:
- apiKeyAuth: []
- name: Management API > Storage
description: Operations for managing storage.
- name: Client API > File Upload
description: Operations for uploading files.
paths:
/responses/{responseId}:
/{environmentId}/responses/{responseId}:
put:
description: Update an existing response for example when you want to mark a
response as finished or you want to change an existing response's value.
parameters:
- in: path
name: environmentId
required: true
schema:
type: string
- in: path
name: responseId
required: true
@@ -280,8 +288,246 @@ paths:
servers:
- url: https://app.formbricks.com/api/v2/client
description: Formbricks Client
/{environmentId}/storage:
post:
summary: Upload Private File
description: >
API endpoint for uploading private files. Uploaded files are kept
private so that only users with access to the specified environment can
retrieve them. The endpoint validates the survey ID, file name, and file
type from the request body, and returns a signed URL for S3 uploads
along with a local upload URL.
tags:
- Client API > File Upload
parameters:
- in: path
name: environmentId
required: true
schema:
type: string
description: The ID of the environment.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
surveyId:
type: string
description: The ID of the survey associated with the file.
fileName:
type: string
description: The name of the file to be uploaded.
fileType:
type: string
description: The MIME type of the file.
required:
- surveyId
- fileName
- fileType
example:
surveyId: cm7pr0x2y004o192zmit8cjvb
fileName: example.jpg
fileType: image/jpeg
responses:
"200":
description: OK - Returns the signed URL, signing data, updated file name, and file URL.
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
signedUrl:
type: string
description: Signed URL for uploading the file to local storage.
signingData:
type: object
properties:
signature:
type: string
description: Signature for verifying the upload.
timestamp:
type: number
description: Timestamp used in the signature.
uuid:
type: string
description: Unique identifier for the signed upload.
updatedFileName:
type: string
description: The updated file name after processing.
fileUrl:
type: string
description: URL where the uploaded file can be accessed.
example:
data:
signedUrl: "http://localhost:3000/api/v1/client/cm1ubebtj000614kqe4hs3c67/storage/local"
signingData:
signature: "3e51c6f441e646a0c9a47fdcdd25eee9bfac26d5506461d811b9c55cbdd90914"
timestamp: 1741693207760
uuid: "f48bcb1aad904f574069a253388024af"
updatedFileName: "halle--fid--b153ba3e-6602-4bb3-bed9-211b5b1ae463.jpg"
fileUrl: "http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/private/halle--fid--b153ba3e-6602-4bb3-bed9-211b5b1ae463.jpg"
"400":
description: Bad Request - One or more required fields are missing.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: fileName is required
"404":
description: Not Found - The specified survey or organization does not exist.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: Survey survey123 not found
servers:
- url: https://app.formbricks.com/api/v2/client
description: Formbricks API Server
/{environmentId}/storage/local:
post:
summary: Upload Private File to Local Storage
description: >
API endpoint for uploading private files to local storage. The request must include a valid signature,
UUID, and timestamp to verify the upload. The file is provided as a Base64 encoded string in the request body.
The "Content-Type" header must be set to a valid MIME type, and the file data must be a valid file object (buffer).
tags:
- Client API > File Upload
parameters:
- in: path
name: environmentId
required: true
schema:
type: string
description: The ID of the environment.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
surveyId:
type: string
description: The ID of the survey associated with the file.
fileName:
type: string
description: The URI encoded file name.
fileType:
type: string
description: The MIME type of the file.
signature:
type: string
description: Signed signature for verifying the file upload.
uuid:
type: string
description: Unique identifier used in the signature validation.
timestamp:
type: string
description: Timestamp used in the signature validation.
fileBase64String:
type: string
description: >
Base64 encoded string of the file. It should include data type information,
e.g. "data:<mime-type>;base64,<base64-encoded-data>".
required:
- surveyId
- fileName
- fileType
- signature
- uuid
- timestamp
- fileBase64String
example:
surveyId: "survey123"
fileName: "example.jpg"
fileType: "image/jpeg"
signature: "signedSignatureValue"
uuid: "uniqueUuidValue"
timestamp: "1627891234567"
fileBase64String: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/..."
responses:
"200":
description: OK - File uploaded successfully.
content:
application/json:
schema:
type: object
properties:
message:
type: string
description: Success message.
example:
message: "File uploaded successfully"
"400":
description: Bad Request - One or more required fields are missing or the file is too large.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "fileName is required"
"401":
description: Unauthorized - Signature validation failed or required signature fields are missing.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "Unauthorized"
"404":
description: Not Found - The specified survey or organization does not exist.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "Survey survey123 not found"
"500":
description: Internal Server Error - File upload failed.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "File upload failed"
servers:
- url: https://app.formbricks.com/api/v2
description: Formbricks API Server
/responses:
get:
security:
- apiKeyAuth: []
operationId: getResponses
summary: Get responses
description: Gets responses from the database.
@@ -347,6 +593,8 @@ paths:
items:
$ref: "#/components/schemas/response"
post:
security:
- apiKeyAuth: []
operationId: createResponse
summary: Create a response
description: Creates a response in the database.
@@ -368,6 +616,8 @@ paths:
$ref: "#/components/schemas/response"
/responses/{id}:
get:
security:
- apiKeyAuth: []
operationId: getResponse
summary: Get a response
description: Gets a response from the database.
@@ -388,6 +638,8 @@ paths:
schema:
$ref: "#/components/schemas/response"
put:
security:
- apiKeyAuth: []
operationId: updateResponse
summary: Update a response
description: Updates a response in the database.
@@ -491,6 +743,8 @@ paths:
schema:
$ref: "#/components/schemas/response"
delete:
security:
- apiKeyAuth: []
operationId: deleteResponse
summary: Delete a response
description: Deletes a response from the database.
@@ -512,6 +766,8 @@ paths:
$ref: "#/components/schemas/response"
/contacts:
get:
security:
- apiKeyAuth: []
operationId: getContacts
summary: Get contacts
description: Gets contacts from the database.
@@ -565,6 +821,8 @@ paths:
items:
$ref: "#/components/schemas/contact"
post:
security:
- apiKeyAuth: []
operationId: createContact
summary: Create a contact
description: Creates a contact in the database.
@@ -586,6 +844,8 @@ paths:
$ref: "#/components/schemas/contact"
/contacts/{id}:
get:
security:
- apiKeyAuth: []
operationId: getContact
summary: Get a contact
description: Gets a contact from the database.
@@ -605,6 +865,8 @@ paths:
schema:
$ref: "#/components/schemas/contact"
put:
security:
- apiKeyAuth: []
operationId: updateContact
summary: Update a contact
description: Updates a contact in the database.
@@ -631,6 +893,8 @@ paths:
schema:
$ref: "#/components/schemas/contact"
delete:
security:
- apiKeyAuth: []
operationId: deleteContact
summary: Delete a contact
description: Deletes a contact from the database.
@@ -651,6 +915,8 @@ paths:
$ref: "#/components/schemas/contact"
/contact-attributes:
get:
security:
- apiKeyAuth: []
operationId: getContactAttributes
summary: Get contact attributes
description: Gets contact attributes from the database.
@@ -704,6 +970,8 @@ paths:
items:
$ref: "#/components/schemas/contactAttribute"
post:
security:
- apiKeyAuth: []
operationId: createContactAttribute
summary: Create a contact attribute
description: Creates a contact attribute in the database.
@@ -721,6 +989,8 @@ paths:
description: Contact attribute created successfully.
/contact-attributes/{id}:
get:
security:
- apiKeyAuth: []
operationId: getContactAttribute
summary: Get a contact attribute
description: Gets a contact attribute from the database.
@@ -740,6 +1010,8 @@ paths:
schema:
$ref: "#/components/schemas/contactAttribute"
put:
security:
- apiKeyAuth: []
operationId: updateContactAttribute
summary: Update a contact attribute
description: Updates a contact attribute in the database.
@@ -766,6 +1038,8 @@ paths:
schema:
$ref: "#/components/schemas/contactAttribute"
delete:
security:
- apiKeyAuth: []
operationId: deleteContactAttribute
summary: Delete a contact attribute
description: Deletes a contact attribute from the database.
@@ -786,6 +1060,8 @@ paths:
$ref: "#/components/schemas/contactAttribute"
/contact-attribute-keys:
get:
security:
- apiKeyAuth: []
operationId: getContactAttributeKeys
summary: Get contact attribute keys
description: Gets contact attribute keys from the database.
@@ -839,6 +1115,8 @@ paths:
items:
$ref: "#/components/schemas/contactAttributeKey"
post:
security:
- apiKeyAuth: []
operationId: createContactAttributeKey
summary: Create a contact attribute key
description: Creates a contact attribute key in the database.
@@ -856,6 +1134,8 @@ paths:
description: Contact attribute key created successfully.
/contact-attribute-keys/{id}:
get:
security:
- apiKeyAuth: []
operationId: getContactAttributeKey
summary: Get a contact attribute key
description: Gets a contact attribute key from the database.
@@ -875,6 +1155,8 @@ paths:
schema:
$ref: "#/components/schemas/contactAttributeKey"
put:
security:
- apiKeyAuth: []
operationId: updateContactAttributeKey
summary: Update a contact attribute key
description: Updates a contact attribute key in the database.
@@ -901,6 +1183,8 @@ paths:
schema:
$ref: "#/components/schemas/contactAttributeKey"
delete:
security:
- apiKeyAuth: []
operationId: deleteContactAttributeKey
summary: Delete a contact attribute key
description: Deletes a contact attribute key from the database.
@@ -921,6 +1205,8 @@ paths:
$ref: "#/components/schemas/contactAttributeKey"
/surveys:
get:
security:
- apiKeyAuth: []
operationId: getSurveys
summary: Get surveys
description: Gets surveys from the database.
@@ -991,6 +1277,8 @@ paths:
items:
$ref: "#/components/schemas/survey"
post:
security:
- apiKeyAuth: []
operationId: createSurvey
summary: Create a survey
description: Creates a survey in the database.
@@ -1012,6 +1300,8 @@ paths:
$ref: "#/components/schemas/survey"
/surveys/{id}:
get:
security:
- apiKeyAuth: []
operationId: getSurvey
summary: Get a survey
description: Gets a survey from the database.
@@ -1032,6 +1322,8 @@ paths:
schema:
$ref: "#/components/schemas/survey"
put:
security:
- apiKeyAuth: []
operationId: updateSurvey
summary: Update a survey
description: Updates a survey in the database.
@@ -1059,6 +1351,8 @@ paths:
schema:
$ref: "#/components/schemas/survey"
delete:
security:
- apiKeyAuth: []
operationId: deleteSurvey
summary: Delete a survey
description: Deletes a survey from the database.
@@ -1078,6 +1372,230 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/survey"
/api/v2/management/storage:
post:
security:
- apiKeyAuth: []
summary: Upload Public File
description: >
API endpoint for uploading public files. Uploaded files are public and accessible by anyone.
This endpoint requires authentication. It accepts a JSON body with fileName, fileType, environmentId,
and optionally allowedFileExtensions to restrict file types. On success, it returns a signed URL for uploading
the file to S3 along with a local upload URL.
tags:
- Management API > Storage
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
fileName:
type: string
description: The name of the file to be uploaded.
fileType:
type: string
description: The MIME type of the file.
environmentId:
type: string
description: The ID of the environment.
allowedFileExtensions:
type: array
items:
type: string
description: Optional. List of allowed file extensions.
required:
- fileName
- fileType
- environmentId
example:
fileName: "profile.png"
fileType: "image/png"
environmentId: "env123"
allowedFileExtensions: ["png", "jpg", "jpeg"]
responses:
"200":
description: OK - Returns the signed URL, updated file name, and file URL.
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
signedUrl:
type: string
description: Signed URL for uploading the file to S3.
localUrl:
type: string
description: URL for uploading the file to local storage.
updatedFileName:
type: string
description: The updated file name after processing.
fileUrl:
type: string
description: URL where the uploaded file can be accessed.
example:
data:
signedUrl: "http://localhost:3000/api/v1/client/cm1ubebtj000614kqe4hs3c67/storage/public"
localUrl: "http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/public/profile.png"
updatedFileName: "profile--fid--abc123.png"
fileUrl: "http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/public/profile--fid--abc123.png"
"400":
description: Bad Request - Missing required fields or invalid file extension.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "fileName is required"
"401":
description: Unauthorized - Authentication failed or user not logged in.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "Not authenticated"
"403":
description: Forbidden - User does not have access to the specified environment.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "User does not have access to environment env123"
/api/v2/management/storage/local:
put:
summary: Upload Public File to Local Storage
security:
- apiKeyAuth: []
description: >
Management API endpoint for uploading public files to local storage. This endpoint requires authentication.
File metadata is provided via headers (X-File-Type, X-File-Name, X-Environment-ID, X-Signature, X-UUID, X-Timestamp)
and the file is provided as a multipart/form-data file field named "file". The "Content-Type" header must be set to a valid MIME type.
tags:
- Management API > Storage
parameters:
- in: header
name: X-File-Type
required: true
schema:
type: string
description: "MIME type of the file. Must be a valid MIME type."
- in: header
name: X-File-Name
required: true
schema:
type: string
description: "URI encoded file name."
- in: header
name: X-Environment-ID
required: true
schema:
type: string
description: "ID of the environment."
- in: header
name: X-Signature
required: true
schema:
type: string
description: "Signature for verifying the request."
- in: header
name: X-UUID
required: true
schema:
type: string
description: "Unique identifier for the signed upload."
- in: header
name: X-Timestamp
required: true
schema:
type: string
description: "Timestamp used for the signature."
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
description: "The file to be uploaded as a valid file object (buffer)."
required:
- file
responses:
"200":
description: OK - File uploaded successfully.
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
message:
type: string
description: Success message.
example:
data:
message: "File uploaded successfully"
"400":
description: Bad Request - Missing required fields, invalid header values, or file issues.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "fileType is required"
"401":
description: Unauthorized - Authentication failed, invalid signature, or user not authorized.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "Not authenticated"
"500":
description: Internal Server Error - File upload failed due to server error.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Detailed error message.
example:
error: "File upload failed"
servers:
- url: https://app.formbricks.com/api/v2
description: Formbricks API Server
components:
securitySchemes:
apiKeyAuth:

View File

@@ -16,7 +16,16 @@ export const ZUploadFileConfig = z.object({
export type TUploadFileConfig = z.infer<typeof ZUploadFileConfig>;
const ZUploadFileResponse = z.object({
export const ZUploadFileRequest = z.object({
fileName: z.string(),
fileType: z.string(),
surveyId: z.string().cuid2(),
environmentId: z.string().cuid2(),
});
export type TUploadFileRequest = z.infer<typeof ZUploadFileRequest>;
export const ZUploadFileResponse = z.object({
data: z.object({
signedUrl: z.string(),
fileUrl: z.string(),