@drodil/backstage-plugin-qeta-backend 3.55.2 → 3.55.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/database/stores/BaseStore.cjs.js +18 -1
- package/dist/database/stores/BaseStore.cjs.js.map +1 -1
- package/dist/service/NotificationManager.cjs.js +26 -26
- package/dist/service/NotificationManager.cjs.js.map +1 -1
- package/dist/service/routes/helpers.cjs.js +9 -5
- package/dist/service/routes/helpers.cjs.js.map +1 -1
- package/dist/service/routes/posts.cjs.js +3 -0
- package/dist/service/routes/posts.cjs.js.map +1 -1
- package/dist/service/types.cjs.js +5 -0
- package/dist/service/types.cjs.js.map +1 -1
- package/migrations/20260108_pg_trgm_extension.js +1 -1
- package/package.json +6 -6
|
@@ -9,7 +9,9 @@ function isQetaFilter(filter) {
|
|
|
9
9
|
class BaseStore {
|
|
10
10
|
constructor(db) {
|
|
11
11
|
this.db = db;
|
|
12
|
+
this.initializePgTrgm();
|
|
12
13
|
}
|
|
14
|
+
pgTrgmAvailable = false;
|
|
13
15
|
mapToInteger(val) {
|
|
14
16
|
return typeof val === "string" ? Number.parseInt(val, 10) : val ?? 0;
|
|
15
17
|
}
|
|
@@ -20,7 +22,7 @@ class BaseStore {
|
|
|
20
22
|
return typeof val === "number" ? val > 0 : !!val;
|
|
21
23
|
}
|
|
22
24
|
applySearchQuery(query, columns, searchQuery) {
|
|
23
|
-
if (this.db.client.config.client === "pg") {
|
|
25
|
+
if (this.db.client.config.client === "pg" && this.pgTrgmAvailable) {
|
|
24
26
|
const terms = searchQuery.trim().split(/\s+/);
|
|
25
27
|
query.andWhere((builder) => {
|
|
26
28
|
const coalescedColumns = columns.map((col) => `COALESCE(${col}::text, '')`).join(` || ' ' || `);
|
|
@@ -124,6 +126,21 @@ class BaseStore {
|
|
|
124
126
|
}
|
|
125
127
|
});
|
|
126
128
|
}
|
|
129
|
+
async initializePgTrgm() {
|
|
130
|
+
if (this.db.client.config.client === "pg") {
|
|
131
|
+
try {
|
|
132
|
+
const result = await this.db.raw(`
|
|
133
|
+
SELECT EXISTS (
|
|
134
|
+
SELECT 1 FROM pg_extension WHERE extname = 'pg_trgm'
|
|
135
|
+
) as available
|
|
136
|
+
`);
|
|
137
|
+
this.pgTrgmAvailable = result.rows?.[0]?.available ?? false;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.warn(`Failed to check for pg_trgm extension: ${error}`);
|
|
140
|
+
this.pgTrgmAvailable = false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
127
144
|
}
|
|
128
145
|
|
|
129
146
|
exports.BaseStore = BaseStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseStore.cjs.js","sources":["../../../src/database/stores/BaseStore.ts"],"sourcesContent":["import { Knex } from 'knex';\nimport { QetaFilter, QetaFilters } from '../../service/util';\nimport { PermissionCriteria } from '@backstage/plugin-permission-common';\nimport {\n isAndCriteria,\n isNotCriteria,\n isOrCriteria,\n} from '@backstage/plugin-permission-node';\nimport { compact } from 'lodash';\n\nfunction isQetaFilter(filter: any): filter is QetaFilter {\n return filter.hasOwnProperty('property');\n}\n\nexport abstract class BaseStore {\n constructor(protected readonly db: Knex) {}\n\n protected mapToInteger(val: string | number | undefined): number {\n return typeof val === 'string' ? Number.parseInt(val, 10) : val ?? 0;\n }\n\n protected mapToBoolean(val: string | number | boolean | undefined): boolean {\n if (typeof val === 'string') {\n return val === 'true' || val === '1';\n }\n return typeof val === 'number' ? val > 0 : !!val;\n }\n\n protected applySearchQuery(\n query: Knex.QueryBuilder,\n columns: string[],\n searchQuery: string,\n ) {\n if (this.db.client.config.client === 'pg') {\n const terms = searchQuery.trim().split(/\\s+/);\n\n query.andWhere(builder => {\n const coalescedColumns = columns\n .map(col => `COALESCE(${col}::text, '')`)\n .join(` || ' ' || `);\n\n terms.forEach(term => {\n builder.andWhereRaw(`(${coalescedColumns}) %> ?`, [term]);\n });\n\n const concatenatedColumns = `LOWER(CONCAT(${columns.join(',')}))`;\n terms.forEach(term => {\n builder.andWhereRaw(`${concatenatedColumns} LIKE LOWER(?)`, [\n `%${term}%`,\n ]);\n });\n });\n } else {\n query.andWhere(builder => {\n const terms = searchQuery.trim().split(/\\s+/);\n\n terms.forEach(term => {\n builder.orWhereRaw(\n `LOWER(CONCAT(${columns.join(',')})) LIKE LOWER(?)`,\n [`%${term}%`],\n );\n });\n });\n }\n }\n\n protected async updateAttachments(\n key: 'postId' | 'answerId' | 'collectionId',\n content: string,\n images: number[],\n id: number,\n headerImage?: string,\n ) {\n if (images.length > 0) {\n await this.db('attachments')\n .whereIn('id', images)\n .update({ [key]: id });\n }\n\n const attachments = await this.db('attachments')\n .where(key, id)\n .select('uuid');\n const uuids = attachments.map(a => a.uuid);\n const toRemove = uuids.filter(uuid => {\n return !(content.includes(uuid) || headerImage?.includes(uuid));\n });\n await this.db('attachments')\n .whereIn('uuid', toRemove)\n .update({ [key]: null });\n }\n\n protected parseFilter(\n filter: PermissionCriteria<QetaFilters>,\n query: Knex.QueryBuilder,\n db: Knex,\n type: 'post' | 'answer' | 'collection' | 'tags' | 'comments' = 'post',\n negate: boolean = false,\n ): Knex.QueryBuilder {\n if (isNotCriteria(filter)) {\n return this.parseFilter(filter.not, query, db, type, !negate);\n }\n\n if (isQetaFilter(filter)) {\n const values: string[] = compact(filter.values) ?? [];\n\n let fk = 'posts.id';\n if (type === 'answer') {\n fk = 'answers.postId';\n } else if (type === 'collection') {\n fk = 'collection_posts.postId';\n }\n if (filter.property === 'tags') {\n const postIds = db('tags')\n .leftJoin('post_tags', 'tags.id', 'post_tags.tagId')\n .where('tags.tag', 'in', values)\n .select('post_tags.postId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, postIds);\n return query;\n }\n if (filter.property === 'tag.experts') {\n if (type === 'post') {\n const postIds = db('tags')\n .leftJoin('post_tags', 'tags.id', 'post_tags.tagId')\n .leftJoin('tag_experts', 'tag_experts.tagId', 'tags.id')\n .where('tag_experts.entityRef', 'in', values)\n .select('post_tags.postId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, postIds);\n return query;\n } else if (type === 'answer') {\n const answerIds = db('answers')\n .leftJoin('posts', 'answers.postId', 'posts.id')\n .leftJoin('post_tags', 'post_tags.postId', 'posts.id')\n .leftJoin('tags', 'post_tags.tagId', 'tags.id')\n .leftJoin('tag_experts', 'tag_experts.tagId', 'tags.id')\n .where('tag_experts.entityRef', 'in', values)\n .select('answers.id');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, answerIds);\n return query;\n } else if (type === 'tags') {\n const tagIds = db('tag_experts')\n .leftJoin('tags', 'tag_experts.tagId', 'tags.id')\n .where('tag_experts.entityRef', 'in', values)\n .select('tag_experts.tagId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, tagIds);\n return query;\n } else if (type === 'collection') {\n const collectionIds = db('collection_posts')\n .leftJoin('posts', 'collection_posts.postId', 'posts.id')\n .leftJoin('post_tags', 'post_tags.postId', 'posts.id')\n .leftJoin('tags', 'post_tags.tagId', 'tags.id')\n .leftJoin('tag_experts', 'tag_experts.tagId', 'tags.id')\n .where('tag_experts.entityRef', 'in', values)\n .select('collection_posts.collectionId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, collectionIds);\n return query;\n }\n }\n\n if (filter.property === 'entityRefs') {\n const postIds = db('entities')\n .leftJoin('post_entities', 'entities.id', 'post_entities.entityId')\n .where('entities.entity_ref', 'in', values)\n .select('post_entities.postId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, postIds);\n return query;\n }\n\n if (values.length === 0) {\n return negate\n ? query.whereNotNull(filter.property)\n : query.whereNull(filter.property);\n }\n\n return query[negate ? 'whereNotIn' : 'whereIn'](filter.property, values);\n }\n\n return query[negate ? 'andWhereNot' : 'andWhere'](builder => {\n const f = filter as PermissionCriteria<QetaFilters>;\n if (isOrCriteria(f)) {\n for (const subFilter of f.anyOf ?? []) {\n builder.orWhere((subQuery: Knex.QueryBuilder) =>\n this.parseFilter(subFilter, subQuery, db, type, false),\n );\n }\n } else if (isAndCriteria(f)) {\n for (const subFilter of f.allOf ?? []) {\n builder.andWhere((subQuery: Knex.QueryBuilder) =>\n this.parseFilter(subFilter, subQuery, db, type, false),\n );\n }\n } else if (isNotCriteria(f)) {\n builder.whereNot((subQuery: Knex.QueryBuilder) =>\n this.parseFilter(f.not, subQuery, db, type, false),\n );\n }\n });\n }\n}\n"],"names":["isNotCriteria","compact","isOrCriteria","isAndCriteria"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BaseStore.cjs.js","sources":["../../../src/database/stores/BaseStore.ts"],"sourcesContent":["import { Knex } from 'knex';\nimport { QetaFilter, QetaFilters } from '../../service/util';\nimport { PermissionCriteria } from '@backstage/plugin-permission-common';\nimport {\n isAndCriteria,\n isNotCriteria,\n isOrCriteria,\n} from '@backstage/plugin-permission-node';\nimport { compact } from 'lodash';\n\nfunction isQetaFilter(filter: any): filter is QetaFilter {\n return filter.hasOwnProperty('property');\n}\n\nexport abstract class BaseStore {\n private pgTrgmAvailable: boolean = false;\n\n constructor(protected readonly db: Knex) {\n this.initializePgTrgm();\n }\n\n protected mapToInteger(val: string | number | undefined): number {\n return typeof val === 'string' ? Number.parseInt(val, 10) : val ?? 0;\n }\n\n protected mapToBoolean(val: string | number | boolean | undefined): boolean {\n if (typeof val === 'string') {\n return val === 'true' || val === '1';\n }\n return typeof val === 'number' ? val > 0 : !!val;\n }\n\n protected applySearchQuery(\n query: Knex.QueryBuilder,\n columns: string[],\n searchQuery: string,\n ) {\n if (this.db.client.config.client === 'pg' && this.pgTrgmAvailable) {\n const terms = searchQuery.trim().split(/\\s+/);\n\n query.andWhere(builder => {\n const coalescedColumns = columns\n .map(col => `COALESCE(${col}::text, '')`)\n .join(` || ' ' || `);\n\n terms.forEach(term => {\n builder.andWhereRaw(`(${coalescedColumns}) %> ?`, [term]);\n });\n\n const concatenatedColumns = `LOWER(CONCAT(${columns.join(',')}))`;\n terms.forEach(term => {\n builder.andWhereRaw(`${concatenatedColumns} LIKE LOWER(?)`, [\n `%${term}%`,\n ]);\n });\n });\n } else {\n query.andWhere(builder => {\n const terms = searchQuery.trim().split(/\\s+/);\n\n terms.forEach(term => {\n builder.orWhereRaw(\n `LOWER(CONCAT(${columns.join(',')})) LIKE LOWER(?)`,\n [`%${term}%`],\n );\n });\n });\n }\n }\n\n protected async updateAttachments(\n key: 'postId' | 'answerId' | 'collectionId',\n content: string,\n images: number[],\n id: number,\n headerImage?: string,\n ) {\n if (images.length > 0) {\n await this.db('attachments')\n .whereIn('id', images)\n .update({ [key]: id });\n }\n\n const attachments = await this.db('attachments')\n .where(key, id)\n .select('uuid');\n const uuids = attachments.map(a => a.uuid);\n const toRemove = uuids.filter(uuid => {\n return !(content.includes(uuid) || headerImage?.includes(uuid));\n });\n await this.db('attachments')\n .whereIn('uuid', toRemove)\n .update({ [key]: null });\n }\n\n protected parseFilter(\n filter: PermissionCriteria<QetaFilters>,\n query: Knex.QueryBuilder,\n db: Knex,\n type: 'post' | 'answer' | 'collection' | 'tags' | 'comments' = 'post',\n negate: boolean = false,\n ): Knex.QueryBuilder {\n if (isNotCriteria(filter)) {\n return this.parseFilter(filter.not, query, db, type, !negate);\n }\n\n if (isQetaFilter(filter)) {\n const values: string[] = compact(filter.values) ?? [];\n\n let fk = 'posts.id';\n if (type === 'answer') {\n fk = 'answers.postId';\n } else if (type === 'collection') {\n fk = 'collection_posts.postId';\n }\n if (filter.property === 'tags') {\n const postIds = db('tags')\n .leftJoin('post_tags', 'tags.id', 'post_tags.tagId')\n .where('tags.tag', 'in', values)\n .select('post_tags.postId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, postIds);\n return query;\n }\n if (filter.property === 'tag.experts') {\n if (type === 'post') {\n const postIds = db('tags')\n .leftJoin('post_tags', 'tags.id', 'post_tags.tagId')\n .leftJoin('tag_experts', 'tag_experts.tagId', 'tags.id')\n .where('tag_experts.entityRef', 'in', values)\n .select('post_tags.postId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, postIds);\n return query;\n } else if (type === 'answer') {\n const answerIds = db('answers')\n .leftJoin('posts', 'answers.postId', 'posts.id')\n .leftJoin('post_tags', 'post_tags.postId', 'posts.id')\n .leftJoin('tags', 'post_tags.tagId', 'tags.id')\n .leftJoin('tag_experts', 'tag_experts.tagId', 'tags.id')\n .where('tag_experts.entityRef', 'in', values)\n .select('answers.id');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, answerIds);\n return query;\n } else if (type === 'tags') {\n const tagIds = db('tag_experts')\n .leftJoin('tags', 'tag_experts.tagId', 'tags.id')\n .where('tag_experts.entityRef', 'in', values)\n .select('tag_experts.tagId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, tagIds);\n return query;\n } else if (type === 'collection') {\n const collectionIds = db('collection_posts')\n .leftJoin('posts', 'collection_posts.postId', 'posts.id')\n .leftJoin('post_tags', 'post_tags.postId', 'posts.id')\n .leftJoin('tags', 'post_tags.tagId', 'tags.id')\n .leftJoin('tag_experts', 'tag_experts.tagId', 'tags.id')\n .where('tag_experts.entityRef', 'in', values)\n .select('collection_posts.collectionId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, collectionIds);\n return query;\n }\n }\n\n if (filter.property === 'entityRefs') {\n const postIds = db('entities')\n .leftJoin('post_entities', 'entities.id', 'post_entities.entityId')\n .where('entities.entity_ref', 'in', values)\n .select('post_entities.postId');\n query[negate ? 'whereNotIn' : 'whereIn'](fk, postIds);\n return query;\n }\n\n if (values.length === 0) {\n return negate\n ? query.whereNotNull(filter.property)\n : query.whereNull(filter.property);\n }\n\n return query[negate ? 'whereNotIn' : 'whereIn'](filter.property, values);\n }\n\n return query[negate ? 'andWhereNot' : 'andWhere'](builder => {\n const f = filter as PermissionCriteria<QetaFilters>;\n if (isOrCriteria(f)) {\n for (const subFilter of f.anyOf ?? []) {\n builder.orWhere((subQuery: Knex.QueryBuilder) =>\n this.parseFilter(subFilter, subQuery, db, type, false),\n );\n }\n } else if (isAndCriteria(f)) {\n for (const subFilter of f.allOf ?? []) {\n builder.andWhere((subQuery: Knex.QueryBuilder) =>\n this.parseFilter(subFilter, subQuery, db, type, false),\n );\n }\n } else if (isNotCriteria(f)) {\n builder.whereNot((subQuery: Knex.QueryBuilder) =>\n this.parseFilter(f.not, subQuery, db, type, false),\n );\n }\n });\n }\n\n private async initializePgTrgm() {\n if (this.db.client.config.client === 'pg') {\n try {\n const result = await this.db.raw(`\n SELECT EXISTS (\n SELECT 1 FROM pg_extension WHERE extname = 'pg_trgm'\n ) as available\n `);\n this.pgTrgmAvailable = result.rows?.[0]?.available ?? false;\n } catch (error) {\n console.warn(`Failed to check for pg_trgm extension: ${error}`);\n this.pgTrgmAvailable = false;\n }\n }\n }\n}\n"],"names":["isNotCriteria","compact","isOrCriteria","isAndCriteria"],"mappings":"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"}
|
|
@@ -515,6 +515,32 @@ class NotificationManager {
|
|
|
515
515
|
}
|
|
516
516
|
return followingUsers;
|
|
517
517
|
}
|
|
518
|
+
async onBadgeAwarded(userRef, badge) {
|
|
519
|
+
if (!this.notifications || !this.enabled) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
try {
|
|
523
|
+
const baseUrl = this.config.getOptionalString("qeta.route") ?? "qeta";
|
|
524
|
+
const link = `/${baseUrl}/users/${encodeURIComponent(userRef)}`;
|
|
525
|
+
await this.notifications.send({
|
|
526
|
+
recipients: {
|
|
527
|
+
type: "entity",
|
|
528
|
+
entityRef: [userRef]
|
|
529
|
+
},
|
|
530
|
+
payload: {
|
|
531
|
+
title: `New badge earned: ${badge.badge.name}`,
|
|
532
|
+
description: this.formatDescription(
|
|
533
|
+
`Congratulations! You've earned the "${badge.badge.name}" badge: ${badge.badge.description}`
|
|
534
|
+
),
|
|
535
|
+
link,
|
|
536
|
+
topic: "Badge awarded",
|
|
537
|
+
scope: `badge:${badge.badge.key}`
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
} catch (e) {
|
|
541
|
+
this.logger.error(`Failed to send notification for badge award: ${e}`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
518
544
|
async getUserDisplayName(username) {
|
|
519
545
|
try {
|
|
520
546
|
const cached = await this.cache?.get(
|
|
@@ -540,32 +566,6 @@ class NotificationManager {
|
|
|
540
566
|
}
|
|
541
567
|
return username;
|
|
542
568
|
}
|
|
543
|
-
async onBadgeAwarded(userRef, badge) {
|
|
544
|
-
if (!this.notifications || !this.enabled) {
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
try {
|
|
548
|
-
const baseUrl = this.config.getOptionalString("qeta.baseUrl") ?? "/qeta";
|
|
549
|
-
const link = `${baseUrl}/users/${encodeURIComponent(userRef)}`;
|
|
550
|
-
await this.notifications.send({
|
|
551
|
-
recipients: {
|
|
552
|
-
type: "entity",
|
|
553
|
-
entityRef: [userRef]
|
|
554
|
-
},
|
|
555
|
-
payload: {
|
|
556
|
-
title: `New badge earned: ${badge.badge.name}`,
|
|
557
|
-
description: this.formatDescription(
|
|
558
|
-
`Congratulations! You've earned the "${badge.badge.name}" badge: ${badge.badge.description}`
|
|
559
|
-
),
|
|
560
|
-
link,
|
|
561
|
-
topic: "Badge awarded",
|
|
562
|
-
scope: `badge:${badge.badge.key}`
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
} catch (e) {
|
|
566
|
-
this.logger.error(`Failed to send notification for badge award: ${e}`);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
569
|
formatDescription(description) {
|
|
570
570
|
return backstagePluginQetaCommon.truncate(backstagePluginQetaCommon.removeMarkdownFormatting(description), 150);
|
|
571
571
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationManager.cjs.js","sources":["../../src/service/NotificationManager.ts"],"sourcesContent":["import { NotificationService } from '@backstage/plugin-notifications-node';\nimport {\n Answer,\n Collection,\n Post,\n removeMarkdownFormatting,\n selectByPostType,\n truncate,\n UserBadge,\n} from '@drodil/backstage-plugin-qeta-common';\nimport {\n AuthService,\n CacheService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { UserEntity } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { NotificationReceiversHandler } from '@drodil/backstage-plugin-qeta-node';\nimport { getResourceUrl } from './util';\n\nexport class NotificationManager {\n private readonly enabled: boolean;\n\n constructor(\n private readonly logger: LoggerService,\n private readonly catalog: CatalogApi,\n private readonly auth: AuthService,\n private readonly config: Config,\n private readonly notifications?: NotificationService,\n private readonly cache?: CacheService,\n private readonly notificationReceivers?: NotificationReceiversHandler,\n ) {\n this.enabled = config.getOptionalBoolean('qeta.notifications') ?? true;\n }\n\n async onNewPost(\n username: string,\n post: Post,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n ...(post?.entities ?? []),\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewPost?.(post)) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onCreate.${post.type}`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New ${post.type}`,\n description: this.formatDescription(\n selectByPostType(\n post.type,\n `${user} asked a question: ${post.title}`,\n `${user} wrote an article: ${post.title}`,\n `${user} created a link: ${post.title}`,\n ),\n ),\n link: getResourceUrl(post, this.config),\n topic: `New ${post.type} about entity`,\n },\n });\n } catch (e) {\n this.logger.error(\n `Failed to send notification for new ${post.type}: ${e}`,\n );\n }\n return notificationReceivers;\n }\n\n async onNewPostComment(\n username: string,\n post: Post,\n comment: string,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const commenters = new Set<string>(post.comments?.map(c => c.author));\n\n const notificationReceivers = [\n ...new Set<string>([\n post.author,\n ...(post?.entities ?? []),\n ...commenters,\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewPostComment?.(post)) ?? []),\n ...(this.config.getOptionalStringArray(\n 'qeta.notificationSettings.onCreate.comment',\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New comment on ${post.type}`,\n description: this.formatDescription(\n `${user} commented on ${post.type} \"${post.title}\": ${comment}`,\n ),\n link: getResourceUrl(post, this.config),\n topic: `New ${post.type} comment`,\n scope: `${post.type}:comment:${post.id}`,\n },\n });\n } catch (e) {\n this.logger.error(\n `Failed to send notification for new post comment: ${e}`,\n );\n }\n return notificationReceivers;\n }\n\n async onPostDelete(\n username: string,\n post: Post,\n reason?: string,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || post.author === username) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n post.author,\n ...((await this.notificationReceivers?.onPostDelete?.(post)) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onDelete.${post.type}`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n },\n payload: {\n title: `Deleted ${post.type}`,\n description: this.formatDescription(\n `${user} deleted your ${post.type} \"${post.title}\" with reason: ${\n reason || 'No reason provided'\n }`,\n ),\n link: getResourceUrl(post, this.config),\n topic: `${post.type} deleted`,\n scope: `${post.type}:delete:${post.id}`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for post delete: ${e}`);\n }\n return [post.author];\n }\n\n async onCollectionDelete(\n username: string,\n collection: Collection,\n reason?: string,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || collection.owner === username) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n collection.owner,\n ...((await this.notificationReceivers?.onCollectionDelete?.(\n collection,\n )) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onDelete.collection`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n },\n payload: {\n title: `Deleted collection`,\n description: this.formatDescription(\n `${user} deleted your collection \"${\n collection.title\n }\" with reason: ${reason || 'No reason provided'}`,\n ),\n link: getResourceUrl(collection, this.config),\n topic: `Collection deleted`,\n scope: `collection:delete:${collection.id}`,\n },\n });\n } catch (e) {\n this.logger.error(\n `Failed to send notification for collection delete: ${e}`,\n );\n }\n return [collection.owner];\n }\n\n async onAnswerDelete(\n username: string,\n post: Post,\n answer: Answer,\n reason?: string,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || answer.author === username) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n answer.author,\n ...((await this.notificationReceivers?.onAnswerDelete?.(\n post,\n answer,\n )) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onDelete.answer`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n },\n payload: {\n title: `Deleted answer`,\n description: this.formatDescription(\n `${user} deleted your answer from question \"${\n post.title\n }\" with reason: ${reason || 'No reason provided'}`,\n ),\n link: getResourceUrl(post, this.config),\n topic: `Answer deleted`,\n scope: `answer:delete:${answer.id}`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for answer delete: ${e}`);\n }\n return [answer.author];\n }\n\n async onPostEdit(\n username: string,\n post: Post,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([post.author, ...followingUsers]),\n ...((await this.notificationReceivers?.onPostEdit?.(post)) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onUpdate.${post.type}`,\n ) ?? []),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `Edit for ${post.type}`,\n description: this.formatDescription(\n `${user} edited ${post.type}: ${post.title}`,\n ),\n link: getResourceUrl(post, this.config),\n topic: `${post.type} edited`,\n scope: `${post.type}:edit:${post.id}`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for post edit: ${e}`);\n }\n return notificationReceivers;\n }\n\n async onNewAnswer(\n username: string,\n question: Post,\n answer: Answer,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n question.author,\n ...(question?.entities ?? []),\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewAnswer?.(\n question,\n answer,\n )) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onCreate.answer`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New answer on question`,\n description: this.formatDescription(\n `${user} answered question \"${question.title}\": ${answer.content}`,\n ),\n link: getResourceUrl(answer, this.config),\n topic: 'New answer on question',\n scope: `question:answer:${question.id}:author`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for new answer: ${e}`);\n }\n return notificationReceivers;\n }\n\n async onAnswerComment(\n username: string,\n question: Post,\n answer: Answer,\n comment: string,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const commenters = new Set<string>(answer.comments?.map(c => c.author));\n\n const notificationReceivers = [\n ...new Set<string>([\n answer.author,\n ...commenters,\n ...(question?.entities ?? []),\n ...followingUsers,\n ...((await this.notificationReceivers?.onAnswerComment?.(\n question,\n answer,\n )) ?? []),\n ...(this.config.getOptionalStringArray(\n 'qeta.notificationSettings.onCreate.comment',\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New comment on answer`,\n description: this.formatDescription(\n `${user} commented on answer to \"${question.title}\": ${comment}`,\n ),\n link: getResourceUrl(answer, this.config),\n topic: 'New answer comment',\n scope: `answer:comment:${answer.id}`,\n },\n });\n } catch (e) {\n this.logger.error(\n `Failed to send notification for new answer comment: ${e}`,\n );\n }\n return notificationReceivers;\n }\n\n async onCorrectAnswer(\n username: string,\n question: Post,\n answer: Answer,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n answer.author,\n question.author,\n ...(question?.entities ?? []),\n ...((await this.notificationReceivers?.onCorrectAnswer?.(\n question,\n answer,\n )) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `Correct answer on question`,\n description: this.formatDescription(\n `${user} marked answer as correct: ${answer.content}`,\n ),\n link: getResourceUrl(answer, this.config),\n topic: 'Correct answer on question',\n scope: `question:correct:${question.id}:answer`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for correct answer: ${e}`);\n }\n return notificationReceivers;\n }\n\n async onMention(\n username: string,\n post: Post | Answer,\n mentions: string[],\n alreadySent: string[],\n isComment?: boolean,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n ...mentions.map(m => m.replaceAll('@', '')),\n ...((await this.notificationReceivers?.onMention?.(post)) ?? []),\n ]),\n ].filter(m => !alreadySent.includes(m));\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n const isPost = 'title' in post;\n const description = isPost\n ? `${user} mentioned you in a post${isComment ? ' comment' : ''}: ${\n post.title\n }`\n : `${user} mentioned you in an answer${isComment ? ' comment' : ''}: ${\n post.content\n }`;\n const link = getResourceUrl(post, this.config, false);\n const scope = isPost\n ? `post:mention:${post.id}`\n : `answer:mention:${post.id}`;\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New mention`,\n description: this.formatDescription(description),\n link,\n topic: 'New mention',\n scope,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for mentions: ${e}`);\n }\n return notificationReceivers;\n }\n\n async onNewCollection(\n username: string,\n collection: Collection,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || followingUsers.length === 0) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewCollection?.(collection)) ??\n []),\n ...(this.config.getOptionalStringArray(\n 'qeta.notificationSettings.onCreate.collection',\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n const description = `${user} created a new collection: ${collection.title}`;\n const link = getResourceUrl(collection, this.config);\n const scope = `collection:${collection.id}:created`;\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New collection`,\n description: this.formatDescription(description),\n link,\n topic: 'New collection',\n scope,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for collection: ${e}`);\n }\n return followingUsers;\n }\n\n async onNewPostToCollection(\n username: string,\n collection: Collection,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || followingUsers.length === 0) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewCollection?.(collection)) ??\n []),\n ...(this.config.getOptionalStringArray(\n 'qeta.notificationSettings.onUpdate.collection',\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n const description = `${user} added a new post to ${collection.title}`;\n const link = getResourceUrl(collection, this.config);\n const scope = `collection:${collection.id}:new_post`;\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New post in collection`,\n description: this.formatDescription(description),\n link,\n topic: 'New post in collection',\n scope,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for collection: ${e}`);\n }\n return followingUsers;\n }\n\n private async getUserDisplayName(username: string) {\n try {\n const cached = await this.cache?.get<string>(\n `user:displayName:${username}`,\n );\n if (cached) {\n return cached;\n }\n\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const entity = await this.catalog.getEntityByRef(username, { token });\n if (entity) {\n const displayName =\n (entity as UserEntity).spec?.profile?.displayName ??\n entity.metadata.title ??\n entity.metadata.name;\n await this.cache?.set(`user:displayName:${username}`, displayName, {\n ttl: 1000 * 60 * 60 * 24,\n });\n return displayName;\n }\n } catch (e) {\n console.error(e);\n }\n return username;\n }\n\n async onBadgeAwarded(userRef: string, badge: UserBadge): Promise<void> {\n if (!this.notifications || !this.enabled) {\n return;\n }\n\n try {\n const baseUrl = this.config.getOptionalString('qeta.baseUrl') ?? '/qeta';\n const link = `${baseUrl}/users/${encodeURIComponent(userRef)}`;\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: [userRef],\n },\n payload: {\n title: `New badge earned: ${badge.badge.name}`,\n description: this.formatDescription(\n `Congratulations! You've earned the \"${badge.badge.name}\" badge: ${badge.badge.description}`,\n ),\n link,\n topic: 'Badge awarded',\n scope: `badge:${badge.badge.key}`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for badge award: ${e}`);\n }\n }\n\n private formatDescription(description: string) {\n return truncate(removeMarkdownFormatting(description), 150);\n }\n}\n"],"names":["selectByPostType","getResourceUrl","truncate","removeMarkdownFormatting"],"mappings":";;;;;AAqBO,MAAM,mBAAoB,CAAA;AAAA,EAG/B,YACmB,MACA,EAAA,OAAA,EACA,MACA,MACA,EAAA,aAAA,EACA,OACA,qBACjB,EAAA;AAPiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA;AACA,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AACA,IAAA,IAAA,CAAA,qBAAA,GAAA,qBAAA;AAEjB,IAAA,IAAA,CAAK,OAAU,GAAA,MAAA,CAAO,kBAAmB,CAAA,oBAAoB,CAAK,IAAA,IAAA;AAAA;AACpE,EAZiB,OAAA;AAAA,EAcjB,MAAM,SAAA,CACJ,QACA,EAAA,IAAA,EACA,cACmB,EAAA;AACnB,IAAA,IAAI,CAAC,IAAA,CAAK,aAAiB,IAAA,CAAC,KAAK,OAAS,EAAA;AACxC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,GAAI,IAAM,EAAA,QAAA,IAAY,EAAC;AAAA,QACvB,GAAG,cAAA;AAAA,QACH,GAAK,MAAM,IAAA,CAAK,uBAAuB,SAAY,GAAA,IAAI,KAAM,EAAC;AAAA,QAC9D,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,UACd,CAAA,mCAAA,EAAsC,KAAK,IAAI,CAAA;AAAA,aAC5C;AAAC,OACP;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA,qBAAA;AAAA,UACX,gBAAkB,EAAA;AAAA,SACpB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAA,EAAO,CAAO,IAAA,EAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAAA,UACvB,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChBA,0CAAA;AAAA,cACE,IAAK,CAAA,IAAA;AAAA,cACL,CAAG,EAAA,IAAI,CAAsB,mBAAA,EAAA,IAAA,CAAK,KAAK,CAAA,CAAA;AAAA,cACvC,CAAG,EAAA,IAAI,CAAsB,mBAAA,EAAA,IAAA,CAAK,KAAK,CAAA,CAAA;AAAA,cACvC,CAAG,EAAA,IAAI,CAAoB,iBAAA,EAAA,IAAA,CAAK,KAAK,CAAA;AAAA;AACvC,WACF;AAAA,UACA,IAAM,EAAAC,mBAAA,CAAe,IAAM,EAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UACtC,KAAA,EAAO,CAAO,IAAA,EAAA,IAAA,CAAK,IAAI,CAAA,aAAA;AAAA;AACzB,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA;AAAA,QACV,CAAuC,oCAAA,EAAA,IAAA,CAAK,IAAI,CAAA,EAAA,EAAK,CAAC,CAAA;AAAA,OACxD;AAAA;AAEF,IAAO,OAAA,qBAAA;AAAA;AACT,EAEA,MAAM,gBAAA,CACJ,QACA,EAAA,IAAA,EACA,SACA,cACmB,EAAA;AACnB,IAAA,IAAI,CAAC,IAAA,CAAK,aAAiB,IAAA,CAAC,KAAK,OAAS,EAAA;AACxC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAM,MAAA,UAAA,GAAa,IAAI,GAAY,CAAA,IAAA,CAAK,UAAU,GAAI,CAAA,CAAA,CAAA,KAAK,CAAE,CAAA,MAAM,CAAC,CAAA;AAEpE,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,IAAK,CAAA,MAAA;AAAA,QACL,GAAI,IAAM,EAAA,QAAA,IAAY,EAAC;AAAA,QACvB,GAAG,UAAA;AAAA,QACH,GAAG,cAAA;AAAA,QACH,GAAK,MAAM,IAAA,CAAK,uBAAuB,gBAAmB,GAAA,IAAI,KAAM,EAAC;AAAA,QACrE,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,UACd;AAAA,aACG;AAAC,OACP;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA,qBAAA;AAAA,UACX,gBAAkB,EAAA;AAAA,SACpB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAA,EAAO,CAAkB,eAAA,EAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAAA,UAClC,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChB,CAAA,EAAG,IAAI,CAAiB,cAAA,EAAA,IAAA,CAAK,IAAI,CAAK,EAAA,EAAA,IAAA,CAAK,KAAK,CAAA,GAAA,EAAM,OAAO,CAAA;AAAA,WAC/D;AAAA,UACA,IAAM,EAAAA,mBAAA,CAAe,IAAM,EAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UACtC,KAAA,EAAO,CAAO,IAAA,EAAA,IAAA,CAAK,IAAI,CAAA,QAAA,CAAA;AAAA,UACvB,OAAO,CAAG,EAAA,IAAA,CAAK,IAAI,CAAA,SAAA,EAAY,KAAK,EAAE,CAAA;AAAA;AACxC,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA;AAAA,QACV,qDAAqD,CAAC,CAAA;AAAA,OACxD;AAAA;AAEF,IAAO,OAAA,qBAAA;AAAA;AACT,EAEA,MAAM,YAAA,CACJ,QACA,EAAA,IAAA,EACA,MACmB,EAAA;AACnB,IAAI,IAAA,CAAC,KAAK,aAAiB,IAAA,CAAC,KAAK,OAAW,IAAA,IAAA,CAAK,WAAW,QAAU,EAAA;AACpE,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,IAAK,CAAA,MAAA;AAAA,QACL,GAAK,MAAM,IAAA,CAAK,uBAAuB,YAAe,GAAA,IAAI,KAAM,EAAC;AAAA,QACjE,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,UACd,CAAA,mCAAA,EAAsC,KAAK,IAAI,CAAA;AAAA,aAC5C;AAAC,OACP;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA;AAAA,SACb;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAA,EAAO,CAAW,QAAA,EAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAAA,UAC3B,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChB,CAAA,EAAG,IAAI,CAAA,cAAA,EAAiB,IAAK,CAAA,IAAI,KAAK,IAAK,CAAA,KAAK,CAC9C,eAAA,EAAA,MAAA,IAAU,oBACZ,CAAA;AAAA,WACF;AAAA,UACA,IAAM,EAAAA,mBAAA,CAAe,IAAM,EAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UACtC,KAAA,EAAO,CAAG,EAAA,IAAA,CAAK,IAAI,CAAA,QAAA,CAAA;AAAA,UACnB,OAAO,CAAG,EAAA,IAAA,CAAK,IAAI,CAAA,QAAA,EAAW,KAAK,EAAE,CAAA;AAAA;AACvC,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAAgD,6CAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AAAA;AAEvE,IAAO,OAAA,CAAC,KAAK,MAAM,CAAA;AAAA;AACrB,EAEA,MAAM,kBAAA,CACJ,QACA,EAAA,UAAA,EACA,MACmB,EAAA;AACnB,IAAI,IAAA,CAAC,KAAK,aAAiB,IAAA,CAAC,KAAK,OAAW,IAAA,UAAA,CAAW,UAAU,QAAU,EAAA;AACzE,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,UAAW,CAAA,KAAA;AAAA,QACX,GAAK,MAAM,IAAA,CAAK,qBAAuB,EAAA,kBAAA;AAAA,UACrC;AAAA,aACI,EAAC;AAAA,QACP,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,UACd,CAAA,6CAAA;AAAA,aACG;AAAC,OACP;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA;AAAA,SACb;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAO,EAAA,CAAA,kBAAA,CAAA;AAAA,UACP,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChB,GAAG,IAAI,CAAA,0BAAA,EACL,WAAW,KACb,CAAA,eAAA,EAAkB,UAAU,oBAAoB,CAAA;AAAA,WAClD;AAAA,UACA,IAAM,EAAAA,mBAAA,CAAe,UAAY,EAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UAC5C,KAAO,EAAA,CAAA,kBAAA,CAAA;AAAA,UACP,KAAA,EAAO,CAAqB,kBAAA,EAAA,UAAA,CAAW,EAAE,CAAA;AAAA;AAC3C,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA;AAAA,QACV,sDAAsD,CAAC,CAAA;AAAA,OACzD;AAAA;AAEF,IAAO,OAAA,CAAC,WAAW,KAAK,CAAA;AAAA;AAC1B,EAEA,MAAM,cAAA,CACJ,QACA,EAAA,IAAA,EACA,QACA,MACmB,EAAA;AACnB,IAAI,IAAA,CAAC,KAAK,aAAiB,IAAA,CAAC,KAAK,OAAW,IAAA,MAAA,CAAO,WAAW,QAAU,EAAA;AACtE,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,MAAO,CAAA,MAAA;AAAA,QACP,GAAK,MAAM,IAAA,CAAK,qBAAuB,EAAA,cAAA;AAAA,UACrC,IAAA;AAAA,UACA;AAAA,aACI,EAAC;AAAA,QACP,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,UACd,CAAA,yCAAA;AAAA,aACG;AAAC,OACP;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA;AAAA,SACb;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAO,EAAA,CAAA,cAAA,CAAA;AAAA,UACP,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChB,GAAG,IAAI,CAAA,oCAAA,EACL,KAAK,KACP,CAAA,eAAA,EAAkB,UAAU,oBAAoB,CAAA;AAAA,WAClD;AAAA,UACA,IAAM,EAAAA,mBAAA,CAAe,IAAM,EAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UACtC,KAAO,EAAA,CAAA,cAAA,CAAA;AAAA,UACP,KAAA,EAAO,CAAiB,cAAA,EAAA,MAAA,CAAO,EAAE,CAAA;AAAA;AACnC,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAAkD,+CAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AAAA;AAEzE,IAAO,OAAA,CAAC,OAAO,MAAM,CAAA;AAAA;AACvB,EAEA,MAAM,UAAA,CACJ,QACA,EAAA,IAAA,EACA,cACmB,EAAA;AACnB,IAAA,IAAI,CAAC,IAAA,CAAK,aAAiB,IAAA,CAAC,KAAK,OAAS,EAAA;AACxC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA,CAAC,KAAK,MAAQ,EAAA,GAAG,cAAc,CAAC,CAAA;AAAA,MACnD,GAAK,MAAM,IAAA,CAAK,uBAAuB,UAAa,GAAA,IAAI,KAAM,EAAC;AAAA,MAC/D,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,QACd,CAAA,mCAAA,EAAsC,KAAK,IAAI,CAAA;AAAA,WAC5C;AAAC,KACR;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA,qBAAA;AAAA,UACX,gBAAkB,EAAA;AAAA,SACpB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAA,EAAO,CAAY,SAAA,EAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAAA,UAC5B,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChB,GAAG,IAAI,CAAA,QAAA,EAAW,KAAK,IAAI,CAAA,EAAA,EAAK,KAAK,KAAK,CAAA;AAAA,WAC5C;AAAA,UACA,IAAM,EAAAA,mBAAA,CAAe,IAAM,EAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UACtC,KAAA,EAAO,CAAG,EAAA,IAAA,CAAK,IAAI,CAAA,OAAA,CAAA;AAAA,UACnB,OAAO,CAAG,EAAA,IAAA,CAAK,IAAI,CAAA,MAAA,EAAS,KAAK,EAAE,CAAA;AAAA;AACrC,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAA8C,2CAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AAAA;AAErE,IAAO,OAAA,qBAAA;AAAA;AACT,EAEA,MAAM,WAAA,CACJ,QACA,EAAA,QAAA,EACA,QACA,cACmB,EAAA;AACnB,IAAA,IAAI,CAAC,IAAA,CAAK,aAAiB,IAAA,CAAC,KAAK,OAAS,EAAA;AACxC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,QAAS,CAAA,MAAA;AAAA,QACT,GAAI,QAAU,EAAA,QAAA,IAAY,EAAC;AAAA,QAC3B,GAAG,cAAA;AAAA,QACH,GAAK,MAAM,IAAA,CAAK,qBAAuB,EAAA,WAAA;AAAA,UACrC,QAAA;AAAA,UACA;AAAA,aACI,EAAC;AAAA,QACP,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,UACd,CAAA,yCAAA;AAAA,aACG;AAAC,OACP;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA,qBAAA;AAAA,UACX,gBAAkB,EAAA;AAAA,SACpB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAO,EAAA,CAAA,sBAAA,CAAA;AAAA,UACP,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChB,GAAG,IAAI,CAAA,oBAAA,EAAuB,SAAS,KAAK,CAAA,GAAA,EAAM,OAAO,OAAO,CAAA;AAAA,WAClE;AAAA,UACA,IAAM,EAAAA,mBAAA,CAAe,MAAQ,EAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UACxC,KAAO,EAAA,wBAAA;AAAA,UACP,KAAA,EAAO,CAAmB,gBAAA,EAAA,QAAA,CAAS,EAAE,CAAA,OAAA;AAAA;AACvC,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAA+C,4CAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AAAA;AAEtE,IAAO,OAAA,qBAAA;AAAA;AACT,EAEA,MAAM,eACJ,CAAA,QAAA,EACA,QACA,EAAA,MAAA,EACA,SACA,cACmB,EAAA;AACnB,IAAA,IAAI,CAAC,IAAA,CAAK,aAAiB,IAAA,CAAC,KAAK,OAAS,EAAA;AACxC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAM,MAAA,UAAA,GAAa,IAAI,GAAY,CAAA,MAAA,CAAO,UAAU,GAAI,CAAA,CAAA,CAAA,KAAK,CAAE,CAAA,MAAM,CAAC,CAAA;AAEtE,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,MAAO,CAAA,MAAA;AAAA,QACP,GAAG,UAAA;AAAA,QACH,GAAI,QAAU,EAAA,QAAA,IAAY,EAAC;AAAA,QAC3B,GAAG,cAAA;AAAA,QACH,GAAK,MAAM,IAAA,CAAK,qBAAuB,EAAA,eAAA;AAAA,UACrC,QAAA;AAAA,UACA;AAAA,aACI,EAAC;AAAA,QACP,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,UACd;AAAA,aACG;AAAC,OACP;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA,qBAAA;AAAA,UACX,gBAAkB,EAAA;AAAA,SACpB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAO,EAAA,CAAA,qBAAA,CAAA;AAAA,UACP,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChB,GAAG,IAAI,CAAA,yBAAA,EAA4B,QAAS,CAAA,KAAK,MAAM,OAAO,CAAA;AAAA,WAChE;AAAA,UACA,IAAM,EAAAA,mBAAA,CAAe,MAAQ,EAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UACxC,KAAO,EAAA,oBAAA;AAAA,UACP,KAAA,EAAO,CAAkB,eAAA,EAAA,MAAA,CAAO,EAAE,CAAA;AAAA;AACpC,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA;AAAA,QACV,uDAAuD,CAAC,CAAA;AAAA,OAC1D;AAAA;AAEF,IAAO,OAAA,qBAAA;AAAA;AACT,EAEA,MAAM,eAAA,CACJ,QACA,EAAA,QAAA,EACA,MACmB,EAAA;AACnB,IAAA,IAAI,CAAC,IAAA,CAAK,aAAiB,IAAA,CAAC,KAAK,OAAS,EAAA;AACxC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,MAAO,CAAA,MAAA;AAAA,QACP,QAAS,CAAA,MAAA;AAAA,QACT,GAAI,QAAU,EAAA,QAAA,IAAY,EAAC;AAAA,QAC3B,GAAK,MAAM,IAAA,CAAK,qBAAuB,EAAA,eAAA;AAAA,UACrC,QAAA;AAAA,UACA;AAAA,aACI;AAAC,OACR;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA,qBAAA;AAAA,UACX,gBAAkB,EAAA;AAAA,SACpB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAO,EAAA,CAAA,0BAAA,CAAA;AAAA,UACP,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChB,CAAG,EAAA,IAAI,CAA8B,2BAAA,EAAA,MAAA,CAAO,OAAO,CAAA;AAAA,WACrD;AAAA,UACA,IAAM,EAAAA,mBAAA,CAAe,MAAQ,EAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UACxC,KAAO,EAAA,4BAAA;AAAA,UACP,KAAA,EAAO,CAAoB,iBAAA,EAAA,QAAA,CAAS,EAAE,CAAA,OAAA;AAAA;AACxC,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAAmD,gDAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AAAA;AAE1E,IAAO,OAAA,qBAAA;AAAA;AACT,EAEA,MAAM,SACJ,CAAA,QAAA,EACA,IACA,EAAA,QAAA,EACA,aACA,SACmB,EAAA;AACnB,IAAA,IAAI,CAAC,IAAA,CAAK,aAAiB,IAAA,CAAC,KAAK,OAAS,EAAA;AACxC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,GAAG,SAAS,GAAI,CAAA,CAAA,CAAA,KAAK,EAAE,UAAW,CAAA,GAAA,EAAK,EAAE,CAAC,CAAA;AAAA,QAC1C,GAAK,MAAM,IAAA,CAAK,uBAAuB,SAAY,GAAA,IAAI,KAAM;AAAC,OAC/D;AAAA,MACD,MAAO,CAAA,CAAA,CAAA,KAAK,CAAC,WAAY,CAAA,QAAA,CAAS,CAAC,CAAC,CAAA;AAEtC,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAA,MAAM,SAAS,OAAW,IAAA,IAAA;AAC1B,MAAM,MAAA,WAAA,GAAc,SAChB,CAAG,EAAA,IAAI,2BAA2B,SAAY,GAAA,UAAA,GAAa,EAAE,CAC3D,EAAA,EAAA,IAAA,CAAK,KACP,CACA,CAAA,GAAA,CAAA,EAAG,IAAI,CAA8B,2BAAA,EAAA,SAAA,GAAY,aAAa,EAAE,CAAA,EAAA,EAC9D,KAAK,OACP,CAAA,CAAA;AACJ,MAAA,MAAM,IAAO,GAAAA,mBAAA,CAAe,IAAM,EAAA,IAAA,CAAK,QAAQ,KAAK,CAAA;AACpD,MAAM,MAAA,KAAA,GAAQ,SACV,CAAgB,aAAA,EAAA,IAAA,CAAK,EAAE,CACvB,CAAA,GAAA,CAAA,eAAA,EAAkB,KAAK,EAAE,CAAA,CAAA;AAE7B,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA,qBAAA;AAAA,UACX,gBAAkB,EAAA;AAAA,SACpB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAO,EAAA,CAAA,WAAA,CAAA;AAAA,UACP,WAAA,EAAa,IAAK,CAAA,iBAAA,CAAkB,WAAW,CAAA;AAAA,UAC/C,IAAA;AAAA,UACA,KAAO,EAAA,aAAA;AAAA,UACP;AAAA;AACF,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAA6C,0CAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AAAA;AAEpE,IAAO,OAAA,qBAAA;AAAA;AACT,EAEA,MAAM,eAAA,CACJ,QACA,EAAA,UAAA,EACA,cACmB,EAAA;AACnB,IAAI,IAAA,CAAC,KAAK,aAAiB,IAAA,CAAC,KAAK,OAAW,IAAA,cAAA,CAAe,WAAW,CAAG,EAAA;AACvE,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,GAAG,cAAA;AAAA,QACH,GAAK,MAAM,IAAA,CAAK,uBAAuB,eAAkB,GAAA,UAAU,KACjE,EAAC;AAAA,QACH,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,UACd;AAAA,aACG;AAAC,OACP;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAA,MAAM,WAAc,GAAA,CAAA,EAAG,IAAI,CAAA,2BAAA,EAA8B,WAAW,KAAK,CAAA,CAAA;AACzE,MAAA,MAAM,IAAO,GAAAA,mBAAA,CAAe,UAAY,EAAA,IAAA,CAAK,MAAM,CAAA;AACnD,MAAM,MAAA,KAAA,GAAQ,CAAc,WAAA,EAAA,UAAA,CAAW,EAAE,CAAA,QAAA,CAAA;AAEzC,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA,qBAAA;AAAA,UACX,gBAAkB,EAAA;AAAA,SACpB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAO,EAAA,CAAA,cAAA,CAAA;AAAA,UACP,WAAA,EAAa,IAAK,CAAA,iBAAA,CAAkB,WAAW,CAAA;AAAA,UAC/C,IAAA;AAAA,UACA,KAAO,EAAA,gBAAA;AAAA,UACP;AAAA;AACF,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAA+C,4CAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AAAA;AAEtE,IAAO,OAAA,cAAA;AAAA;AACT,EAEA,MAAM,qBAAA,CACJ,QACA,EAAA,UAAA,EACA,cACmB,EAAA;AACnB,IAAI,IAAA,CAAC,KAAK,aAAiB,IAAA,CAAC,KAAK,OAAW,IAAA,cAAA,CAAe,WAAW,CAAG,EAAA;AACvE,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,MAAM,qBAAwB,GAAA;AAAA,MAC5B,uBAAO,GAAY,CAAA;AAAA,QACjB,GAAG,cAAA;AAAA,QACH,GAAK,MAAM,IAAA,CAAK,uBAAuB,eAAkB,GAAA,UAAU,KACjE,EAAC;AAAA,QACH,GAAI,KAAK,MAAO,CAAA,sBAAA;AAAA,UACd;AAAA,aACG;AAAC,OACP;AAAA,KACH;AAEA,IAAI,IAAA,qBAAA,CAAsB,WAAW,CAAG,EAAA;AACtC,MAAA,OAAO,EAAC;AAAA;AAGV,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,QAAQ,CAAA;AAEnD,MAAA,MAAM,WAAc,GAAA,CAAA,EAAG,IAAI,CAAA,qBAAA,EAAwB,WAAW,KAAK,CAAA,CAAA;AACnE,MAAA,MAAM,IAAO,GAAAA,mBAAA,CAAe,UAAY,EAAA,IAAA,CAAK,MAAM,CAAA;AACnD,MAAM,MAAA,KAAA,GAAQ,CAAc,WAAA,EAAA,UAAA,CAAW,EAAE,CAAA,SAAA,CAAA;AAEzC,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAW,EAAA,qBAAA;AAAA,UACX,gBAAkB,EAAA;AAAA,SACpB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAO,EAAA,CAAA,sBAAA,CAAA;AAAA,UACP,WAAA,EAAa,IAAK,CAAA,iBAAA,CAAkB,WAAW,CAAA;AAAA,UAC/C,IAAA;AAAA,UACA,KAAO,EAAA,wBAAA;AAAA,UACP;AAAA;AACF,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAA+C,4CAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AAAA;AAEtE,IAAO,OAAA,cAAA;AAAA;AACT,EAEA,MAAc,mBAAmB,QAAkB,EAAA;AACjD,IAAI,IAAA;AACF,MAAM,MAAA,MAAA,GAAS,MAAM,IAAA,CAAK,KAAO,EAAA,GAAA;AAAA,QAC/B,oBAAoB,QAAQ,CAAA;AAAA,OAC9B;AACA,MAAA,IAAI,MAAQ,EAAA;AACV,QAAO,OAAA,MAAA;AAAA;AAGT,MAAA,MAAM,EAAE,KAAM,EAAA,GAAI,MAAM,IAAA,CAAK,KAAK,qBAAsB,CAAA;AAAA,QACtD,UAAY,EAAA,MAAM,IAAK,CAAA,IAAA,CAAK,wBAAyB,EAAA;AAAA,QACrD,cAAgB,EAAA;AAAA,OACjB,CAAA;AACD,MAAM,MAAA,MAAA,GAAS,MAAM,IAAK,CAAA,OAAA,CAAQ,eAAe,QAAU,EAAA,EAAE,OAAO,CAAA;AACpE,MAAA,IAAI,MAAQ,EAAA;AACV,QAAM,MAAA,WAAA,GACH,OAAsB,IAAM,EAAA,OAAA,EAAS,eACtC,MAAO,CAAA,QAAA,CAAS,KAChB,IAAA,MAAA,CAAO,QAAS,CAAA,IAAA;AAClB,QAAA,MAAM,KAAK,KAAO,EAAA,GAAA,CAAI,CAAoB,iBAAA,EAAA,QAAQ,IAAI,WAAa,EAAA;AAAA,UACjE,GAAA,EAAK,GAAO,GAAA,EAAA,GAAK,EAAK,GAAA;AAAA,SACvB,CAAA;AACD,QAAO,OAAA,WAAA;AAAA;AACT,aACO,CAAG,EAAA;AACV,MAAA,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA;AAEjB,IAAO,OAAA,QAAA;AAAA;AACT,EAEA,MAAM,cAAe,CAAA,OAAA,EAAiB,KAAiC,EAAA;AACrE,IAAA,IAAI,CAAC,IAAA,CAAK,aAAiB,IAAA,CAAC,KAAK,OAAS,EAAA;AACxC,MAAA;AAAA;AAGF,IAAI,IAAA;AACF,MAAA,MAAM,OAAU,GAAA,IAAA,CAAK,MAAO,CAAA,iBAAA,CAAkB,cAAc,CAAK,IAAA,OAAA;AACjE,MAAA,MAAM,OAAO,CAAG,EAAA,OAAO,CAAU,OAAA,EAAA,kBAAA,CAAmB,OAAO,CAAC,CAAA,CAAA;AAE5D,MAAM,MAAA,IAAA,CAAK,cAAc,IAAK,CAAA;AAAA,QAC5B,UAAY,EAAA;AAAA,UACV,IAAM,EAAA,QAAA;AAAA,UACN,SAAA,EAAW,CAAC,OAAO;AAAA,SACrB;AAAA,QACA,OAAS,EAAA;AAAA,UACP,KAAO,EAAA,CAAA,kBAAA,EAAqB,KAAM,CAAA,KAAA,CAAM,IAAI,CAAA,CAAA;AAAA,UAC5C,aAAa,IAAK,CAAA,iBAAA;AAAA,YAChB,uCAAuC,KAAM,CAAA,KAAA,CAAM,IAAI,CAAY,SAAA,EAAA,KAAA,CAAM,MAAM,WAAW,CAAA;AAAA,WAC5F;AAAA,UACA,IAAA;AAAA,UACA,KAAO,EAAA,eAAA;AAAA,UACP,KAAO,EAAA,CAAA,MAAA,EAAS,KAAM,CAAA,KAAA,CAAM,GAAG,CAAA;AAAA;AACjC,OACD,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAAgD,6CAAA,EAAA,CAAC,CAAE,CAAA,CAAA;AAAA;AACvE;AACF,EAEQ,kBAAkB,WAAqB,EAAA;AAC7C,IAAA,OAAOC,kCAAS,CAAAC,kDAAA,CAAyB,WAAW,CAAA,EAAG,GAAG,CAAA;AAAA;AAE9D;;;;"}
|
|
1
|
+
{"version":3,"file":"NotificationManager.cjs.js","sources":["../../src/service/NotificationManager.ts"],"sourcesContent":["import { NotificationService } from '@backstage/plugin-notifications-node';\nimport {\n Answer,\n Collection,\n Post,\n removeMarkdownFormatting,\n selectByPostType,\n truncate,\n UserBadge,\n} from '@drodil/backstage-plugin-qeta-common';\nimport {\n AuthService,\n CacheService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { UserEntity } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { NotificationReceiversHandler } from '@drodil/backstage-plugin-qeta-node';\nimport { getResourceUrl } from './util';\n\nexport class NotificationManager {\n private readonly enabled: boolean;\n\n constructor(\n private readonly logger: LoggerService,\n private readonly catalog: CatalogApi,\n private readonly auth: AuthService,\n private readonly config: Config,\n private readonly notifications?: NotificationService,\n private readonly cache?: CacheService,\n private readonly notificationReceivers?: NotificationReceiversHandler,\n ) {\n this.enabled = config.getOptionalBoolean('qeta.notifications') ?? true;\n }\n\n async onNewPost(\n username: string,\n post: Post,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n ...(post?.entities ?? []),\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewPost?.(post)) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onCreate.${post.type}`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New ${post.type}`,\n description: this.formatDescription(\n selectByPostType(\n post.type,\n `${user} asked a question: ${post.title}`,\n `${user} wrote an article: ${post.title}`,\n `${user} created a link: ${post.title}`,\n ),\n ),\n link: getResourceUrl(post, this.config),\n topic: `New ${post.type} about entity`,\n },\n });\n } catch (e) {\n this.logger.error(\n `Failed to send notification for new ${post.type}: ${e}`,\n );\n }\n return notificationReceivers;\n }\n\n async onNewPostComment(\n username: string,\n post: Post,\n comment: string,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const commenters = new Set<string>(post.comments?.map(c => c.author));\n\n const notificationReceivers = [\n ...new Set<string>([\n post.author,\n ...(post?.entities ?? []),\n ...commenters,\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewPostComment?.(post)) ?? []),\n ...(this.config.getOptionalStringArray(\n 'qeta.notificationSettings.onCreate.comment',\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New comment on ${post.type}`,\n description: this.formatDescription(\n `${user} commented on ${post.type} \"${post.title}\": ${comment}`,\n ),\n link: getResourceUrl(post, this.config),\n topic: `New ${post.type} comment`,\n scope: `${post.type}:comment:${post.id}`,\n },\n });\n } catch (e) {\n this.logger.error(\n `Failed to send notification for new post comment: ${e}`,\n );\n }\n return notificationReceivers;\n }\n\n async onPostDelete(\n username: string,\n post: Post,\n reason?: string,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || post.author === username) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n post.author,\n ...((await this.notificationReceivers?.onPostDelete?.(post)) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onDelete.${post.type}`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n },\n payload: {\n title: `Deleted ${post.type}`,\n description: this.formatDescription(\n `${user} deleted your ${post.type} \"${post.title}\" with reason: ${\n reason || 'No reason provided'\n }`,\n ),\n link: getResourceUrl(post, this.config),\n topic: `${post.type} deleted`,\n scope: `${post.type}:delete:${post.id}`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for post delete: ${e}`);\n }\n return [post.author];\n }\n\n async onCollectionDelete(\n username: string,\n collection: Collection,\n reason?: string,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || collection.owner === username) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n collection.owner,\n ...((await this.notificationReceivers?.onCollectionDelete?.(\n collection,\n )) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onDelete.collection`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n },\n payload: {\n title: `Deleted collection`,\n description: this.formatDescription(\n `${user} deleted your collection \"${\n collection.title\n }\" with reason: ${reason || 'No reason provided'}`,\n ),\n link: getResourceUrl(collection, this.config),\n topic: `Collection deleted`,\n scope: `collection:delete:${collection.id}`,\n },\n });\n } catch (e) {\n this.logger.error(\n `Failed to send notification for collection delete: ${e}`,\n );\n }\n return [collection.owner];\n }\n\n async onAnswerDelete(\n username: string,\n post: Post,\n answer: Answer,\n reason?: string,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || answer.author === username) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n answer.author,\n ...((await this.notificationReceivers?.onAnswerDelete?.(\n post,\n answer,\n )) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onDelete.answer`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n },\n payload: {\n title: `Deleted answer`,\n description: this.formatDescription(\n `${user} deleted your answer from question \"${\n post.title\n }\" with reason: ${reason || 'No reason provided'}`,\n ),\n link: getResourceUrl(post, this.config),\n topic: `Answer deleted`,\n scope: `answer:delete:${answer.id}`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for answer delete: ${e}`);\n }\n return [answer.author];\n }\n\n async onPostEdit(\n username: string,\n post: Post,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([post.author, ...followingUsers]),\n ...((await this.notificationReceivers?.onPostEdit?.(post)) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onUpdate.${post.type}`,\n ) ?? []),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `Edit for ${post.type}`,\n description: this.formatDescription(\n `${user} edited ${post.type}: ${post.title}`,\n ),\n link: getResourceUrl(post, this.config),\n topic: `${post.type} edited`,\n scope: `${post.type}:edit:${post.id}`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for post edit: ${e}`);\n }\n return notificationReceivers;\n }\n\n async onNewAnswer(\n username: string,\n question: Post,\n answer: Answer,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n question.author,\n ...(question?.entities ?? []),\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewAnswer?.(\n question,\n answer,\n )) ?? []),\n ...(this.config.getOptionalStringArray(\n `qeta.notificationSettings.onCreate.answer`,\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New answer on question`,\n description: this.formatDescription(\n `${user} answered question \"${question.title}\": ${answer.content}`,\n ),\n link: getResourceUrl(answer, this.config),\n topic: 'New answer on question',\n scope: `question:answer:${question.id}:author`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for new answer: ${e}`);\n }\n return notificationReceivers;\n }\n\n async onAnswerComment(\n username: string,\n question: Post,\n answer: Answer,\n comment: string,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const commenters = new Set<string>(answer.comments?.map(c => c.author));\n\n const notificationReceivers = [\n ...new Set<string>([\n answer.author,\n ...commenters,\n ...(question?.entities ?? []),\n ...followingUsers,\n ...((await this.notificationReceivers?.onAnswerComment?.(\n question,\n answer,\n )) ?? []),\n ...(this.config.getOptionalStringArray(\n 'qeta.notificationSettings.onCreate.comment',\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New comment on answer`,\n description: this.formatDescription(\n `${user} commented on answer to \"${question.title}\": ${comment}`,\n ),\n link: getResourceUrl(answer, this.config),\n topic: 'New answer comment',\n scope: `answer:comment:${answer.id}`,\n },\n });\n } catch (e) {\n this.logger.error(\n `Failed to send notification for new answer comment: ${e}`,\n );\n }\n return notificationReceivers;\n }\n\n async onCorrectAnswer(\n username: string,\n question: Post,\n answer: Answer,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n answer.author,\n question.author,\n ...(question?.entities ?? []),\n ...((await this.notificationReceivers?.onCorrectAnswer?.(\n question,\n answer,\n )) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `Correct answer on question`,\n description: this.formatDescription(\n `${user} marked answer as correct: ${answer.content}`,\n ),\n link: getResourceUrl(answer, this.config),\n topic: 'Correct answer on question',\n scope: `question:correct:${question.id}:answer`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for correct answer: ${e}`);\n }\n return notificationReceivers;\n }\n\n async onMention(\n username: string,\n post: Post | Answer,\n mentions: string[],\n alreadySent: string[],\n isComment?: boolean,\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n ...mentions.map(m => m.replaceAll('@', '')),\n ...((await this.notificationReceivers?.onMention?.(post)) ?? []),\n ]),\n ].filter(m => !alreadySent.includes(m));\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n const isPost = 'title' in post;\n const description = isPost\n ? `${user} mentioned you in a post${isComment ? ' comment' : ''}: ${\n post.title\n }`\n : `${user} mentioned you in an answer${isComment ? ' comment' : ''}: ${\n post.content\n }`;\n const link = getResourceUrl(post, this.config, false);\n const scope = isPost\n ? `post:mention:${post.id}`\n : `answer:mention:${post.id}`;\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New mention`,\n description: this.formatDescription(description),\n link,\n topic: 'New mention',\n scope,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for mentions: ${e}`);\n }\n return notificationReceivers;\n }\n\n async onNewCollection(\n username: string,\n collection: Collection,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || followingUsers.length === 0) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewCollection?.(collection)) ??\n []),\n ...(this.config.getOptionalStringArray(\n 'qeta.notificationSettings.onCreate.collection',\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n const description = `${user} created a new collection: ${collection.title}`;\n const link = getResourceUrl(collection, this.config);\n const scope = `collection:${collection.id}:created`;\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New collection`,\n description: this.formatDescription(description),\n link,\n topic: 'New collection',\n scope,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for collection: ${e}`);\n }\n return followingUsers;\n }\n\n async onNewPostToCollection(\n username: string,\n collection: Collection,\n followingUsers: string[],\n ): Promise<string[]> {\n if (!this.notifications || !this.enabled || followingUsers.length === 0) {\n return [];\n }\n\n const notificationReceivers = [\n ...new Set<string>([\n ...followingUsers,\n ...((await this.notificationReceivers?.onNewCollection?.(collection)) ??\n []),\n ...(this.config.getOptionalStringArray(\n 'qeta.notificationSettings.onUpdate.collection',\n ) ?? []),\n ]),\n ];\n\n if (notificationReceivers.length === 0) {\n return [];\n }\n\n try {\n const user = await this.getUserDisplayName(username);\n\n const description = `${user} added a new post to ${collection.title}`;\n const link = getResourceUrl(collection, this.config);\n const scope = `collection:${collection.id}:new_post`;\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: notificationReceivers,\n excludeEntityRef: username,\n },\n payload: {\n title: `New post in collection`,\n description: this.formatDescription(description),\n link,\n topic: 'New post in collection',\n scope,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for collection: ${e}`);\n }\n return followingUsers;\n }\n\n async onBadgeAwarded(userRef: string, badge: UserBadge): Promise<void> {\n if (!this.notifications || !this.enabled) {\n return;\n }\n\n try {\n const baseUrl = this.config.getOptionalString('qeta.route') ?? 'qeta';\n const link = `/${baseUrl}/users/${encodeURIComponent(userRef)}`;\n\n await this.notifications.send({\n recipients: {\n type: 'entity',\n entityRef: [userRef],\n },\n payload: {\n title: `New badge earned: ${badge.badge.name}`,\n description: this.formatDescription(\n `Congratulations! You've earned the \"${badge.badge.name}\" badge: ${badge.badge.description}`,\n ),\n link,\n topic: 'Badge awarded',\n scope: `badge:${badge.badge.key}`,\n },\n });\n } catch (e) {\n this.logger.error(`Failed to send notification for badge award: ${e}`);\n }\n }\n\n private async getUserDisplayName(username: string) {\n try {\n const cached = await this.cache?.get<string>(\n `user:displayName:${username}`,\n );\n if (cached) {\n return cached;\n }\n\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const entity = await this.catalog.getEntityByRef(username, { token });\n if (entity) {\n const displayName =\n (entity as UserEntity).spec?.profile?.displayName ??\n entity.metadata.title ??\n entity.metadata.name;\n await this.cache?.set(`user:displayName:${username}`, displayName, {\n ttl: 1000 * 60 * 60 * 24,\n });\n return displayName;\n }\n } catch (e) {\n console.error(e);\n }\n return username;\n }\n\n private formatDescription(description: string) {\n return truncate(removeMarkdownFormatting(description), 150);\n }\n}\n"],"names":["selectByPostType","getResourceUrl","truncate","removeMarkdownFormatting"],"mappings":"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"}
|
|
@@ -207,7 +207,7 @@ const helperRoutes = (router, options) => {
|
|
|
207
207
|
},
|
|
208
208
|
{ token }
|
|
209
209
|
);
|
|
210
|
-
const entityTags = entityResponse.items.flatMap((e) => e?.metadata?.tags).filter((t) => !!t).map((tag) => tag.toLocaleLowerCase()).filter(backstagePluginQetaCommon.filterTags)
|
|
210
|
+
const entityTags = entityResponse.items.flatMap((e) => e?.metadata?.tags).filter((t) => !!t).map((tag) => tag.toLocaleLowerCase()).filter(backstagePluginQetaCommon.filterTags);
|
|
211
211
|
suggestedTags.push(...entityTags);
|
|
212
212
|
} catch (_error) {
|
|
213
213
|
}
|
|
@@ -216,7 +216,7 @@ const helperRoutes = (router, options) => {
|
|
|
216
216
|
if (aiHandler?.suggestTags) {
|
|
217
217
|
const { tags } = await aiHandler.suggestTags(title, content);
|
|
218
218
|
suggestedTags.unshift(...tags);
|
|
219
|
-
return [...new Set(suggestedTags)].
|
|
219
|
+
return [...new Set(suggestedTags)].map((tag) => tag.toLocaleLowerCase()).filter(backstagePluginQetaCommon.filterTags);
|
|
220
220
|
}
|
|
221
221
|
} catch (_error) {
|
|
222
222
|
}
|
|
@@ -242,7 +242,7 @@ const helperRoutes = (router, options) => {
|
|
|
242
242
|
suggestedTags.push(tag.tag);
|
|
243
243
|
}
|
|
244
244
|
});
|
|
245
|
-
return [...new Set(suggestedTags)].filter(backstagePluginQetaCommon.filterTags)
|
|
245
|
+
return [...new Set(suggestedTags)].filter(backstagePluginQetaCommon.filterTags);
|
|
246
246
|
};
|
|
247
247
|
router.post("/tags/suggest", async (request, response) => {
|
|
248
248
|
const validateRequestBody = ajv.compile(types.DraftQuestionSchema);
|
|
@@ -259,7 +259,9 @@ const helperRoutes = (router, options) => {
|
|
|
259
259
|
);
|
|
260
260
|
const allTags = request.body.tags ? [.../* @__PURE__ */ new Set([...request.body.tags, ...suggestedTags])] : suggestedTags;
|
|
261
261
|
const uniqueTags = [...new Set(allTags)];
|
|
262
|
-
|
|
262
|
+
const limit = request.body.limit && request.body.limit > 0 ? request.body.limit : uniqueTags.length;
|
|
263
|
+
const randomizedTags = uniqueTags.sort(() => Math.random() - 0.5);
|
|
264
|
+
response.json({ tags: randomizedTags.slice(0, limit) });
|
|
263
265
|
} catch (error) {
|
|
264
266
|
logger.error(`Failed to generate tag suggestions: ${error}`);
|
|
265
267
|
response.status(500).json({ error: "Failed to generate tag suggestions" });
|
|
@@ -474,7 +476,9 @@ const helperRoutes = (router, options) => {
|
|
|
474
476
|
const ref = catalogModel.stringifyEntityRef(entity);
|
|
475
477
|
return !request.body.entities?.includes(ref);
|
|
476
478
|
});
|
|
477
|
-
|
|
479
|
+
const limit = request.body.limit && request.body.limit > 0 ? request.body.limit : notSetEntities.length;
|
|
480
|
+
const randomizedEntities = notSetEntities.sort(() => Math.random() - 0.5);
|
|
481
|
+
response.json({ entities: randomizedEntities.slice(0, limit) });
|
|
478
482
|
} catch (error) {
|
|
479
483
|
logger.error(`Failed to generate entity suggestions: ${error}`);
|
|
480
484
|
response.status(500).json({ error: "Failed to generate entity suggestions" });
|