0.12.18 : add create message, conversation
All checks were successful
Build release Docker image / Build Docker Images (push) Successful in 7m23s
All checks were successful
Build release Docker image / Build Docker Images (push) Successful in 7m23s
This commit is contained in:
@@ -2,6 +2,36 @@
|
||||
* chat-conversation controller
|
||||
*/
|
||||
|
||||
import { factories } from '@strapi/strapi'
|
||||
import { factories } from "@strapi/strapi";
|
||||
|
||||
export default factories.createCoreController('api::chat-conversation.chat-conversation');
|
||||
export default factories.createCoreController(
|
||||
"api::chat-conversation.chat-conversation",
|
||||
({ strapi }) => ({
|
||||
async create(ctx) {
|
||||
const userId = ctx.state.user?.id;
|
||||
if (!userId) {
|
||||
return ctx.unauthorized(
|
||||
"You must be logged in to create a conversation"
|
||||
);
|
||||
}
|
||||
|
||||
const { users, title, isGroup } = ctx.request.body.data;
|
||||
|
||||
if (!users || !Array.isArray(users) || users.length === 0) {
|
||||
return ctx.badRequest("userIds must be a non-empty array");
|
||||
}
|
||||
|
||||
try {
|
||||
const conversation = await strapi
|
||||
.service("api::chat-conversation.chat-conversation")
|
||||
.createConversationWithMembers(users, userId, title);
|
||||
|
||||
ctx.body = {
|
||||
data: conversation,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return ctx.badRequest(error.message);
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -4,4 +4,64 @@
|
||||
|
||||
import { factories } from '@strapi/strapi';
|
||||
|
||||
export default factories.createCoreService('api::chat-conversation.chat-conversation');
|
||||
export default factories.createCoreService(
|
||||
'api::chat-conversation.chat-conversation',
|
||||
({ strapi }) => ({
|
||||
async createConversationWithMembers(
|
||||
userIds: number[],
|
||||
creatorId: number,
|
||||
title?: string
|
||||
) {
|
||||
if (!userIds || userIds.length === 0) {
|
||||
throw new Error('At least one user ID is required');
|
||||
}
|
||||
|
||||
if (!creatorId) {
|
||||
throw new Error('Creator ID is required');
|
||||
}
|
||||
|
||||
const uniqueUserIds = [...new Set([...userIds, creatorId])];
|
||||
|
||||
for (const userId of uniqueUserIds) {
|
||||
const userExists = await strapi.db
|
||||
.query('plugin::users-permissions.user')
|
||||
.findOne({
|
||||
where: { id: userId },
|
||||
});
|
||||
|
||||
if (!userExists) {
|
||||
throw new Error(`User with ID ${userId} not found`);
|
||||
}
|
||||
}
|
||||
|
||||
const isGroup = uniqueUserIds.length > 2;
|
||||
|
||||
const conversation = await strapi.db
|
||||
.query('api::chat-conversation.chat-conversation')
|
||||
.create({
|
||||
data: {
|
||||
title: isGroup ? title : null,
|
||||
isGroup,
|
||||
creator: creatorId,
|
||||
users: uniqueUserIds.map((id) => ({ id })),
|
||||
},
|
||||
});
|
||||
|
||||
for (const userId of uniqueUserIds) {
|
||||
const role = userId === creatorId ? 'owner' : 'member';
|
||||
await strapi.db
|
||||
.query('api::chat-conversation-member.chat-conversation-member')
|
||||
.create({
|
||||
data: {
|
||||
user: userId,
|
||||
conversation: conversation.id,
|
||||
role,
|
||||
joinedAt: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return conversation;
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"info": {
|
||||
"singularName": "chat-message",
|
||||
"pluralName": "chat-messages",
|
||||
"displayName": "ChatMessage"
|
||||
"displayName": "ChatMessage",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"draftAndPublish": false
|
||||
@@ -26,6 +27,16 @@
|
||||
},
|
||||
"deletedAt": {
|
||||
"type": "datetime"
|
||||
},
|
||||
"media": {
|
||||
"allowedTypes": [
|
||||
"images",
|
||||
"files",
|
||||
"videos",
|
||||
"audios"
|
||||
],
|
||||
"type": "media",
|
||||
"multiple": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,87 @@
|
||||
* chat-message controller
|
||||
*/
|
||||
|
||||
import { factories } from '@strapi/strapi'
|
||||
import { factories } from "@strapi/strapi";
|
||||
|
||||
export default factories.createCoreController('api::chat-message.chat-message');
|
||||
export default factories.createCoreController(
|
||||
"api::chat-message.chat-message",
|
||||
({ strapi }) => ({
|
||||
async create(ctx) {
|
||||
const userId = ctx.state.user?.id;
|
||||
if (!userId) {
|
||||
return ctx.unauthorized("You must be logged in to send a message");
|
||||
}
|
||||
|
||||
const body = ctx.request.body as any;
|
||||
const data =
|
||||
typeof body?.data === "string"
|
||||
? JSON.parse(body.data)
|
||||
: body?.data || {};
|
||||
|
||||
const { conversation, content, recipientIds } = data;
|
||||
|
||||
if (!content || content.trim() === "") {
|
||||
return ctx.badRequest("Message content is required");
|
||||
}
|
||||
|
||||
let finalConversationId = conversation;
|
||||
|
||||
if (!finalConversationId) {
|
||||
if (
|
||||
!recipientIds ||
|
||||
!Array.isArray(recipientIds) ||
|
||||
recipientIds.length === 0
|
||||
) {
|
||||
return ctx.badRequest(
|
||||
"Either conversationId or recipientIds must be provided"
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const newConversation = await strapi
|
||||
.service("api::chat-conversation.chat-conversation")
|
||||
.createConversationWithMembers(recipientIds, userId);
|
||||
finalConversationId = newConversation.id;
|
||||
} catch (error: any) {
|
||||
return ctx.badRequest(
|
||||
`Failed to create conversation: ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let mediaId: number | undefined;
|
||||
const mediaInput = (ctx.request.files as any)?.media;
|
||||
if (mediaInput) {
|
||||
const file = Array.isArray(mediaInput) ? mediaInput[0] : mediaInput;
|
||||
const uploaded = await strapi
|
||||
.plugin("upload")
|
||||
.service("upload")
|
||||
.upload({
|
||||
data: {
|
||||
fileInfo: {
|
||||
alternativeText: data?.name || "media",
|
||||
caption: "media",
|
||||
name: file.originalFilename || "media",
|
||||
},
|
||||
},
|
||||
files: file,
|
||||
});
|
||||
|
||||
if (uploaded?.[0]?.id) {
|
||||
mediaId = uploaded[0].id;
|
||||
}
|
||||
}
|
||||
const message = await strapi
|
||||
.service("api::chat-message.chat-message")
|
||||
.createMessageInConversation(finalConversationId, userId, content, mediaId);
|
||||
|
||||
ctx.body = {
|
||||
data: message,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return ctx.badRequest(`Failed to create message: ${error.message}`);
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -4,4 +4,92 @@
|
||||
|
||||
import { factories } from '@strapi/strapi';
|
||||
|
||||
export default factories.createCoreService('api::chat-message.chat-message');
|
||||
export default factories.createCoreService(
|
||||
'api::chat-message.chat-message',
|
||||
({ strapi }) => ({
|
||||
async createMessageInConversation(
|
||||
conversationId: number,
|
||||
senderId: number,
|
||||
content: string,
|
||||
mediaId?: number
|
||||
) {
|
||||
if (!conversationId) {
|
||||
throw new Error('Conversation ID is required');
|
||||
}
|
||||
|
||||
if (!senderId) {
|
||||
throw new Error('Sender ID is required');
|
||||
}
|
||||
|
||||
if (!content || content.trim() === '') {
|
||||
throw new Error('Message content is required and cannot be empty');
|
||||
}
|
||||
|
||||
const conversation = await strapi.db
|
||||
.query('api::chat-conversation.chat-conversation')
|
||||
.findOne({
|
||||
where: { id: conversationId },
|
||||
});
|
||||
|
||||
if (!conversation) {
|
||||
throw new Error(`Conversation with ID ${conversationId} not found`);
|
||||
}
|
||||
|
||||
const member = await strapi.db
|
||||
.query('api::chat-conversation-member.chat-conversation-member')
|
||||
.findOne({
|
||||
where: {
|
||||
user: { id: senderId },
|
||||
conversation: { id: conversationId },
|
||||
},
|
||||
});
|
||||
|
||||
if (!member) {
|
||||
throw new Error(
|
||||
'User is not a member of this conversation'
|
||||
);
|
||||
}
|
||||
|
||||
if (mediaId) {
|
||||
const media = await strapi.db
|
||||
.query('plugin::upload.file')
|
||||
.findOne({
|
||||
where: { id: mediaId },
|
||||
});
|
||||
|
||||
if (!media) {
|
||||
throw new Error(`Media with ID ${mediaId} not found`);
|
||||
}
|
||||
}
|
||||
|
||||
const messageData: any = {
|
||||
sender: senderId,
|
||||
content: content.trim(),
|
||||
isEdited: false,
|
||||
};
|
||||
|
||||
if (mediaId) {
|
||||
messageData.media = mediaId;
|
||||
}
|
||||
|
||||
const message = await strapi.db
|
||||
.query('api::chat-message.chat-message')
|
||||
.create({
|
||||
data: messageData,
|
||||
});
|
||||
|
||||
await strapi.db
|
||||
.query('api::chat-conversation.chat-conversation')
|
||||
.update({
|
||||
where: { id: conversationId },
|
||||
data: {
|
||||
messages: {
|
||||
connect: [{ id: message.id }],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return message;
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"name": "Apache 2.0",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"x-generation-date": "2026-01-15T08:01:40.043Z"
|
||||
"x-generation-date": "2026-01-16T10:01:51.447Z"
|
||||
},
|
||||
"x-strapi-config": {
|
||||
"plugins": [
|
||||
@@ -44201,6 +44201,135 @@
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"media": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeText": {
|
||||
"type": "string"
|
||||
},
|
||||
"caption": {
|
||||
"type": "string"
|
||||
},
|
||||
"width": {
|
||||
"type": "integer"
|
||||
},
|
||||
"height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"formats": {},
|
||||
"hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"ext": {
|
||||
"type": "string"
|
||||
},
|
||||
"mime": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"previewUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider_metadata": {},
|
||||
"related": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"folder": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"publishedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"updatedBy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"locale": {
|
||||
"type": "string"
|
||||
},
|
||||
"localizations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
@@ -48258,6 +48387,135 @@
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"media": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeText": {
|
||||
"type": "string"
|
||||
},
|
||||
"caption": {
|
||||
"type": "string"
|
||||
},
|
||||
"width": {
|
||||
"type": "integer"
|
||||
},
|
||||
"height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"formats": {},
|
||||
"hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"ext": {
|
||||
"type": "string"
|
||||
},
|
||||
"mime": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"previewUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider_metadata": {},
|
||||
"related": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"folder": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"publishedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"updatedBy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"locale": {
|
||||
"type": "string"
|
||||
},
|
||||
"localizations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
@@ -48565,6 +48823,17 @@
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"media": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"example": "string or id"
|
||||
},
|
||||
"locale": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -52286,6 +52555,135 @@
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"media": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeText": {
|
||||
"type": "string"
|
||||
},
|
||||
"caption": {
|
||||
"type": "string"
|
||||
},
|
||||
"width": {
|
||||
"type": "integer"
|
||||
},
|
||||
"height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"formats": {},
|
||||
"hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"ext": {
|
||||
"type": "string"
|
||||
},
|
||||
"mime": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"previewUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider_metadata": {},
|
||||
"related": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"folder": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"publishedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"updatedBy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"locale": {
|
||||
"type": "string"
|
||||
},
|
||||
"localizations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
@@ -52355,6 +52753,135 @@
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"media": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeText": {
|
||||
"type": "string"
|
||||
},
|
||||
"caption": {
|
||||
"type": "string"
|
||||
},
|
||||
"width": {
|
||||
"type": "integer"
|
||||
},
|
||||
"height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"formats": {},
|
||||
"hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"ext": {
|
||||
"type": "string"
|
||||
},
|
||||
"mime": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"previewUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider_metadata": {},
|
||||
"related": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"folder": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"publishedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"updatedBy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"locale": {
|
||||
"type": "string"
|
||||
},
|
||||
"localizations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number"
|
||||
},
|
||||
"documentId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
|
||||
Reference in New Issue
Block a user