From 443f36b07d0ee4754e8d040db19bbe62a19134b5 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Wed, 8 May 2024 11:33:33 +0300 Subject: [PATCH 1/3] feat: add telegram subscription --- .env.example | 9 ++- src/api/notification/services/notification.ts | 3 + .../telegram-subscription/schema.json | 27 ++++--- .../controllers/telegram-subscription.ts | 38 ++++++++- .../routes/telegram-subscription.ts | 47 ++++++++++- .../services/telegram-subscription.ts | 81 ++++++++++++++++++- src/api/telegram-subscription/types.ts | 8 ++ .../1.0.0/full_documentation.json | 56 +++++++++---- 8 files changed, 234 insertions(+), 35 deletions(-) create mode 100644 src/api/telegram-subscription/types.ts diff --git a/.env.example b/.env.example index d0f6f5e..546f173 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,7 @@ # Environment (development, production) NODE_ENV=development +TELEGRAM_SECRET=00000000.xxxxxxxxxxxxxxxxxxx # Host configuration HOST=0.0.0.0 @@ -20,8 +21,8 @@ DATABASE_CLIENT=sqlite # For production, you should use postgresql database (and configure it below) # DATABASE_CLIENT=postgres -# DATABASE_HOST=localhost -# DATABASE_PORT=5432 +# DATABASE_HOST=localhost +# DATABASE_PORT=5432 # DATABASE_NAME=cow-cms # DATABASE_USERNAME=cow # DATABASE_PASSWORD=moooo @@ -36,10 +37,10 @@ DATABASE_CLIENT=sqlite APP_KEYS="toBeModified1,toBeModified2" # The secret keys used to sign session cookies, follows the format: key1, key2, ... # Salt used to generate API tokens -API_TOKEN_SALT=tobemodified +API_TOKEN_SALT=tobemodified # JWT secrets -ADMIN_JWT_SECRET=tobemodified +ADMIN_JWT_SECRET=tobemodified JWT_SECRET=tobemodified # The salt used to generate a transfer token. Transfer tokens are used to migrate data between Strapi instances. diff --git a/src/api/notification/services/notification.ts b/src/api/notification/services/notification.ts index cf815f2..803750c 100644 --- a/src/api/notification/services/notification.ts +++ b/src/api/notification/services/notification.ts @@ -28,6 +28,7 @@ export default factories.createCoreService(MODULE_ID, ({ strapi }) => { return notifications.map(notification => ({ id: notification.id, + account: notification.account, title: notification.notification_template.title, description: templateNotification(notification.notification_template.description, notification.data), url: notification.notification_template.url, @@ -40,6 +41,8 @@ export default factories.createCoreService(MODULE_ID, ({ strapi }) => { function templateNotification(description: string, data: {[key: string]: string}): string { let result = description + if (!data) return result + Object.keys(data).forEach(key => { result = result.replace(`{{ ${key} }}`, data[key]) }) diff --git a/src/api/telegram-subscription/content-types/telegram-subscription/schema.json b/src/api/telegram-subscription/content-types/telegram-subscription/schema.json index 52f88e5..a970dbb 100644 --- a/src/api/telegram-subscription/content-types/telegram-subscription/schema.json +++ b/src/api/telegram-subscription/content-types/telegram-subscription/schema.json @@ -4,7 +4,8 @@ "info": { "singularName": "telegram-subscription", "pluralName": "telegram-subscriptions", - "displayName": "Telegram subscription" + "displayName": "Telegram subscription", + "description": "" }, "options": { "draftAndPublish": true @@ -16,17 +17,23 @@ "required": true, "unique": false }, - "login": { - "type": "string", - "required": true + "auth_date": { + "type": "biginteger" + }, + "first_name": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "chat_id": { + "type": "biginteger" }, - "verified": { - "type": "boolean", - "default": false, - "required": true + "photo_url": { + "type": "string" }, - "verificationCode": { - "type": "integer" + "username": { + "type": "string" } } } diff --git a/src/api/telegram-subscription/controllers/telegram-subscription.ts b/src/api/telegram-subscription/controllers/telegram-subscription.ts index e3e35c2..42389a8 100644 --- a/src/api/telegram-subscription/controllers/telegram-subscription.ts +++ b/src/api/telegram-subscription/controllers/telegram-subscription.ts @@ -3,5 +3,41 @@ */ import { factories } from '@strapi/strapi' +import { errors } from '@strapi/utils' +import { TelegramData } from '../types' -export default factories.createCoreController('api::telegram-subscription.telegram-subscription'); +const MODULE_ID = 'api::telegram-subscription.telegram-subscription' + +export default factories.createCoreController(MODULE_ID, ({strapi}) => { + return { + async addSubscription(context) { + const {account, data} : { account: string, data: TelegramData } = context.request.body + + const service = strapi.service(MODULE_ID) + + const existing = await strapi.entityService.findMany(MODULE_ID, { filters: { account, chat_id: data.id } }) + + if (existing.length > 0) return true + + const result = await service.verifyTgAuthentication(data) + + if (!result) { + throw new errors.ValidationError('Invalid telegram authentication data') + } + + await service.addSubscription(account, data) + + return true + }, + async getSubscriptions(context) { + const account = context.params.account + + return strapi.service(MODULE_ID).getAccountSubscriptions(account) + }, + async sendNotifications(context) { + const account = context.params.account + + return strapi.service(MODULE_ID).sendNotifications(account) + } + } +}); diff --git a/src/api/telegram-subscription/routes/telegram-subscription.ts b/src/api/telegram-subscription/routes/telegram-subscription.ts index e94904a..bb04c1c 100644 --- a/src/api/telegram-subscription/routes/telegram-subscription.ts +++ b/src/api/telegram-subscription/routes/telegram-subscription.ts @@ -4,4 +4,49 @@ import { factories } from '@strapi/strapi'; -export default factories.createCoreRouter('api::telegram-subscription.telegram-subscription'); +const defaultRouter = factories.createCoreRouter('api::telegram-subscription.telegram-subscription'); + +const customRouter = (innerRouter, extraRoutes = []) => { + let routes; + return { + get prefix() { + return innerRouter.prefix; + }, + get routes() { + if (!routes) routes = innerRouter.routes.concat(extraRoutes); + return routes; + }, + }; +}; + +const myExtraRoutes = [ + { + method: 'GET', + path: '/tg-subscriptions/:account', + handler: 'telegram-subscription.getSubscriptions', + config: { + policies: [], + middlewares: [], + }, + }, + { + method: 'POST', + path: '/add-tg-subscription', + handler: 'telegram-subscription.addSubscription', + config: { + policies: [], + middlewares: [], + }, + }, + { + method: 'GET', + path: '/send-tg-notifications/:account', + handler: 'telegram-subscription.sendNotifications', + config: { + policies: [], + middlewares: [], + }, + }, +]; + +export default customRouter(defaultRouter, myExtraRoutes); diff --git a/src/api/telegram-subscription/services/telegram-subscription.ts b/src/api/telegram-subscription/services/telegram-subscription.ts index 2a37cea..6f2ac89 100644 --- a/src/api/telegram-subscription/services/telegram-subscription.ts +++ b/src/api/telegram-subscription/services/telegram-subscription.ts @@ -2,6 +2,83 @@ * telegram-subscription service */ -import { factories } from '@strapi/strapi'; +import { factories } from '@strapi/strapi' +import { env } from '@strapi/utils' +import fetch from 'node-fetch' +import crypto from 'crypto' +import { TelegramData } from '../types' -export default factories.createCoreService('api::telegram-subscription.telegram-subscription'); +const MODULE_ID = 'api::telegram-subscription.telegram-subscription' + +const SEND_MESSAGE_URL = `https://api.telegram.org/bot${env('TELEGRAM_SECRET')}/sendMessage` + +export default factories.createCoreService(MODULE_ID, ({strapi}) => { + return { + async verifyTgAuthentication(data: TelegramData) { + const dataString = Object.keys(data).reduce((acc, key) => { + if (key === 'hash') return acc + + acc.push(`${key}=${data[key]}`) + return acc + }, []).sort().join('\n') + + const secretHash = crypto.createHash('sha256').update(env('TELEGRAM_SECRET')).digest('base64') + const result = crypto.createHmac('sha256', new Buffer(secretHash, 'base64')).update(dataString).digest('hex') + + return result === data.hash + }, + async addSubscription(account: string, data: TelegramData) { + return strapi.entityService.create( + MODULE_ID, + { + data: { + account, + auth_date: data.auth_date, + first_name: data.first_name, + hash: data.hash, + chat_id: data.id, + photo_url: data.photo_url, + username: data.username, + } + }) + }, + async getAccountSubscriptions(account: string) { + return strapi.entityService.findMany( + MODULE_ID, + { + filters: { + account + }, + fields: ['id', 'account', 'chat_id'] + } + ) + }, + // TODO: temporary implementation + async sendNotifications(account: string): Promise { + const notifications = await strapi.service('api::notification.notification').getNotificationList(account, true) + + if (notifications.length === 0) return 0 + + const subscriptions = await this.getAccountSubscriptions(account) + + const requests = subscriptions.map(subscription => { + return notifications.map(notification => { + return fetch(SEND_MESSAGE_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + chat_id: subscription.chat_id, + text: notification.description + }) + }) + }) + }).flat() + + await Promise.all(requests) + + return requests.length + } + } +}); diff --git a/src/api/telegram-subscription/types.ts b/src/api/telegram-subscription/types.ts new file mode 100644 index 0000000..5f64a40 --- /dev/null +++ b/src/api/telegram-subscription/types.ts @@ -0,0 +1,8 @@ +export interface TelegramData { + auth_date: number + first_name: string + hash: string + id: number + photo_url: string + username: string +} diff --git a/src/extensions/documentation/documentation/1.0.0/full_documentation.json b/src/extensions/documentation/documentation/1.0.0/full_documentation.json index 86630db..067a50d 100644 --- a/src/extensions/documentation/documentation/1.0.0/full_documentation.json +++ b/src/extensions/documentation/documentation/1.0.0/full_documentation.json @@ -14,7 +14,7 @@ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" }, - "x-generation-date": "2024-05-07T09:27:31.910Z" + "x-generation-date": "2024-05-08T09:37:18.172Z" }, "x-strapi-config": { "path": "/documentation", @@ -17141,23 +17141,34 @@ "properties": { "data": { "required": [ - "account", - "login", - "verified" + "account" ], "type": "object", "properties": { "account": { "type": "string" }, - "login": { + "auth_date": { + "type": "string", + "pattern": "^\\d*$", + "example": "123456789" + }, + "first_name": { "type": "string" }, - "verified": { - "type": "boolean" + "hash": { + "type": "string" }, - "verificationCode": { - "type": "integer" + "chat_id": { + "type": "string", + "pattern": "^\\d*$", + "example": "123456789" + }, + "photo_url": { + "type": "string" + }, + "username": { + "type": "string" } } } @@ -17212,22 +17223,33 @@ "TelegramSubscription": { "type": "object", "required": [ - "account", - "login", - "verified" + "account" ], "properties": { "account": { "type": "string" }, - "login": { + "auth_date": { + "type": "string", + "pattern": "^\\d*$", + "example": "123456789" + }, + "first_name": { "type": "string" }, - "verified": { - "type": "boolean" + "hash": { + "type": "string" }, - "verificationCode": { - "type": "integer" + "chat_id": { + "type": "string", + "pattern": "^\\d*$", + "example": "123456789" + }, + "photo_url": { + "type": "string" + }, + "username": { + "type": "string" }, "createdAt": { "type": "string", From 3f20b87cc065d4d1c802fd83d0f3a200eec865ee Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Wed, 8 May 2024 12:58:58 +0300 Subject: [PATCH 2/3] feat: add due date --- .../notification-template/schema.json | 3 +++ .../documentation/1.0.0/full_documentation.json | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/api/notification-template/content-types/notification-template/schema.json b/src/api/notification-template/content-types/notification-template/schema.json index 76a8f08..2b6c2c5 100644 --- a/src/api/notification-template/content-types/notification-template/schema.json +++ b/src/api/notification-template/content-types/notification-template/schema.json @@ -26,6 +26,9 @@ }, "thumbnail": { "type": "string" + }, + "due_date": { + "type": "datetime" } } } diff --git a/src/extensions/documentation/documentation/1.0.0/full_documentation.json b/src/extensions/documentation/documentation/1.0.0/full_documentation.json index 067a50d..80463de 100644 --- a/src/extensions/documentation/documentation/1.0.0/full_documentation.json +++ b/src/extensions/documentation/documentation/1.0.0/full_documentation.json @@ -14,7 +14,7 @@ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" }, - "x-generation-date": "2024-05-08T09:37:18.172Z" + "x-generation-date": "2024-05-08T09:51:10.314Z" }, "x-strapi-config": { "path": "/documentation", @@ -11746,6 +11746,10 @@ "thumbnail": { "type": "string" }, + "due_date": { + "type": "string", + "format": "date-time" + }, "createdAt": { "type": "string", "format": "date-time" @@ -12142,6 +12146,10 @@ }, "thumbnail": { "type": "string" + }, + "due_date": { + "type": "string", + "format": "date-time" } } } @@ -12211,6 +12219,10 @@ "thumbnail": { "type": "string" }, + "due_date": { + "type": "string", + "format": "date-time" + }, "createdAt": { "type": "string", "format": "date-time" From 5f323497e2572e2c22c0f97e4c2eb318bf4115ba Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Wed, 8 May 2024 14:32:45 +0300 Subject: [PATCH 3/3] fix: change thumbnail type --- .../notification-template/schema.json | 10 +- .../1.0.0/full_documentation.json | 1452 +++++++++++++---- 2 files changed, 1104 insertions(+), 358 deletions(-) diff --git a/src/api/notification-template/content-types/notification-template/schema.json b/src/api/notification-template/content-types/notification-template/schema.json index 2b6c2c5..cd74701 100644 --- a/src/api/notification-template/content-types/notification-template/schema.json +++ b/src/api/notification-template/content-types/notification-template/schema.json @@ -24,11 +24,15 @@ "push": { "type": "boolean" }, - "thumbnail": { - "type": "string" - }, "due_date": { "type": "datetime" + }, + "thumbnail": { + "allowedTypes": [ + "images" + ], + "type": "media", + "multiple": false } } } diff --git a/src/extensions/documentation/documentation/1.0.0/full_documentation.json b/src/extensions/documentation/documentation/1.0.0/full_documentation.json index 80463de..94bc87d 100644 --- a/src/extensions/documentation/documentation/1.0.0/full_documentation.json +++ b/src/extensions/documentation/documentation/1.0.0/full_documentation.json @@ -14,7 +14,7 @@ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" }, - "x-generation-date": "2024-05-08T09:51:10.314Z" + "x-generation-date": "2024-05-08T11:32:34.749Z" }, "x-strapi-config": { "path": "/documentation", @@ -11743,26 +11743,11 @@ "push": { "type": "boolean" }, - "thumbnail": { - "type": "string" - }, "due_date": { "type": "string", "format": "date-time" }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - }, - "publishedAt": { - "type": "string", - "format": "date-time" - }, - "createdBy": { + "thumbnail": { "type": "object", "properties": { "data": { @@ -11774,29 +11759,46 @@ "attributes": { "type": "object", "properties": { - "firstname": { + "name": { "type": "string" }, - "lastname": { + "alternativeText": { "type": "string" }, - "username": { + "caption": { "type": "string" }, - "email": { - "type": "string", - "format": "email" + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + }, + "formats": {}, + "hash": { + "type": "string" + }, + "ext": { + "type": "string" + }, + "mime": { + "type": "string" + }, + "size": { + "type": "number", + "format": "float" }, - "resetPasswordToken": { + "url": { "type": "string" }, - "registrationToken": { + "previewUrl": { "type": "string" }, - "isActive": { - "type": "boolean" + "provider": { + "type": "string" }, - "roles": { + "provider_metadata": {}, + "related": { "type": "object", "properties": { "data": { @@ -11809,136 +11811,54 @@ }, "attributes": { "type": "object", - "properties": { - "name": { - "type": "string" - }, - "code": { - "type": "string" - }, - "description": { - "type": "string" - }, - "users": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - } - } - } - }, - "permissions": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { + "properties": {} + } + } + } + } + } + }, + "folder": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "pathId": { + "type": "integer" + }, + "parent": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "subject": { - "type": "string" - }, - "properties": {}, - "conditions": {}, - "role": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - } - } - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - }, - "createdBy": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - } - } - }, - "updatedBy": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} - } - } - } - } - } - } - } - } + "properties": {} } } } - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - }, - "createdBy": { - "type": "object", - "properties": { - "data": { + } + }, + "children": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "type": "object", "properties": { "id": { @@ -11951,11 +11871,14 @@ } } } - }, - "updatedBy": { - "type": "object", - "properties": { - "data": { + } + }, + "files": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "type": "object", "properties": { "id": { @@ -11963,45 +11886,460 @@ }, "attributes": { "type": "object", - "properties": {} - } - } - } - } - } - } - } - } - } - } - } - }, - "blocked": { - "type": "boolean" - }, - "preferedLanguage": { - "type": "string" - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - }, - "createdBy": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} + "properties": { + "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": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + }, + "folder": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "folderPath": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": { + "firstname": { + "type": "string" + }, + "lastname": { + "type": "string" + }, + "username": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "resetPasswordToken": { + "type": "string" + }, + "registrationToken": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "roles": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "users": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + }, + "permissions": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "properties": {}, + "conditions": {}, + "role": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + } + } + } + } + } + } + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + } + } + } + } + } + } + }, + "blocked": { + "type": "boolean" + }, + "preferedLanguage": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + } + } + } + } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + } + } + } + } + } + } + }, + "path": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + } + } + } + } + } + }, + "folderPath": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} } } } @@ -12030,6 +12368,35 @@ } } }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "publishedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, "updatedBy": { "type": "object", "properties": { @@ -12144,12 +12511,20 @@ "push": { "type": "boolean" }, - "thumbnail": { - "type": "string" - }, "due_date": { "type": "string", "format": "date-time" + }, + "thumbnail": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "example": "string or id" } } } @@ -12216,26 +12591,11 @@ "push": { "type": "boolean" }, - "thumbnail": { - "type": "string" - }, "due_date": { "type": "string", "format": "date-time" }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - }, - "publishedAt": { - "type": "string", - "format": "date-time" - }, - "createdBy": { + "thumbnail": { "type": "object", "properties": { "data": { @@ -12247,29 +12607,46 @@ "attributes": { "type": "object", "properties": { - "firstname": { + "name": { "type": "string" }, - "lastname": { + "alternativeText": { "type": "string" }, - "username": { + "caption": { "type": "string" }, - "email": { - "type": "string", - "format": "email" + "width": { + "type": "integer" }, - "resetPasswordToken": { + "height": { + "type": "integer" + }, + "formats": {}, + "hash": { "type": "string" }, - "registrationToken": { + "ext": { "type": "string" }, - "isActive": { - "type": "boolean" + "mime": { + "type": "string" }, - "roles": { + "size": { + "type": "number", + "format": "float" + }, + "url": { + "type": "string" + }, + "previewUrl": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "provider_metadata": {}, + "related": { "type": "object", "properties": { "data": { @@ -12282,62 +12659,127 @@ }, "attributes": { "type": "object", - "properties": { - "name": { - "type": "string" - }, - "code": { - "type": "string" - }, - "description": { - "type": "string" - }, - "users": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { + "properties": {} + } + } + } + } + } + }, + "folder": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "pathId": { + "type": "integer" + }, + "parent": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} - } - } + "properties": {} } } } - }, - "permissions": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "subject": { - "type": "string" - }, - "properties": {}, - "conditions": {}, - "role": { - "type": "object", - "properties": { - "data": { + } + }, + "children": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + }, + "files": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": { + "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": "object", + "properties": { + "data": { + "type": "array", + "items": { "type": "object", "properties": { "id": { @@ -12350,45 +12792,316 @@ } } } - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - }, - "createdBy": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} + } + }, + "folder": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "folderPath": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": { + "firstname": { + "type": "string" + }, + "lastname": { + "type": "string" + }, + "username": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "resetPasswordToken": { + "type": "string" + }, + "registrationToken": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "roles": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "users": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + }, + "permissions": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "properties": {}, + "conditions": {}, + "role": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + } + } + } + } + } + } + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } + } + } + } + } + } + } + }, + "blocked": { + "type": "boolean" + }, + "preferedLanguage": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + } } } } } - }, - "updatedBy": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} - } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} } } } @@ -12399,45 +13112,48 @@ } } } - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - }, - "createdBy": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} - } + } + }, + "path": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} } } } - }, - "updatedBy": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "attributes": { - "type": "object", - "properties": {} - } + } + }, + "updatedBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} } } } @@ -12449,10 +13165,7 @@ } } }, - "blocked": { - "type": "boolean" - }, - "preferedLanguage": { + "folderPath": { "type": "string" }, "createdAt": { @@ -12503,6 +13216,35 @@ } } }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "publishedAt": { + "type": "string", + "format": "date-time" + }, + "createdBy": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + } + }, "updatedBy": { "type": "object", "properties": {