actor-html.ts 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import { escapeHTML, maxBy } from '@peertube/peertube-core-utils'
  2. import { HttpStatusCode } from '@peertube/peertube-models'
  3. import { AccountModel } from '@server/models/account/account.js'
  4. import { ActorImageModel } from '@server/models/actor/actor-image.js'
  5. import { VideoChannelModel } from '@server/models/video/video-channel.js'
  6. import { MAccountHost, MChannelHost } from '@server/types/models/index.js'
  7. import express from 'express'
  8. import { CONFIG } from '../../../initializers/config.js'
  9. import { PageHtml } from './page-html.js'
  10. import { TagsHtml } from './tags-html.js'
  11. export class ActorHtml {
  12. static async getAccountHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) {
  13. const accountModelPromise = AccountModel.loadByNameWithHost(nameWithHost)
  14. return this.getAccountOrChannelHTMLPage(() => accountModelPromise, req, res)
  15. }
  16. static async getVideoChannelHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) {
  17. const videoChannelModelPromise = VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithHost)
  18. return this.getAccountOrChannelHTMLPage(() => videoChannelModelPromise, req, res)
  19. }
  20. static async getActorHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) {
  21. const [ account, channel ] = await Promise.all([
  22. AccountModel.loadByNameWithHost(nameWithHost),
  23. VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithHost)
  24. ])
  25. return this.getAccountOrChannelHTMLPage(() => Promise.resolve(account || channel), req, res)
  26. }
  27. // ---------------------------------------------------------------------------
  28. private static async getAccountOrChannelHTMLPage (
  29. loader: () => Promise<MAccountHost | MChannelHost>,
  30. req: express.Request,
  31. res: express.Response
  32. ) {
  33. const [ html, entity ] = await Promise.all([
  34. PageHtml.getIndexHTML(req, res),
  35. loader()
  36. ])
  37. // Let Angular application handle errors
  38. if (!entity) {
  39. res.status(HttpStatusCode.NOT_FOUND_404)
  40. return PageHtml.getIndexHTML(req, res)
  41. }
  42. const escapedTruncatedDescription = TagsHtml.buildEscapedTruncatedDescription(entity.description)
  43. let customHTML = TagsHtml.addTitleTag(html, entity.getDisplayName())
  44. customHTML = TagsHtml.addDescriptionTag(customHTML, escapedTruncatedDescription)
  45. const url = entity.getClientUrl()
  46. const siteName = CONFIG.INSTANCE.NAME
  47. const title = entity.getDisplayName()
  48. const avatar = maxBy(entity.Actor.Avatars, 'width')
  49. const image = {
  50. url: ActorImageModel.getImageUrl(avatar),
  51. width: avatar?.width,
  52. height: avatar?.height
  53. }
  54. const ogType = 'website'
  55. const twitterCard = 'summary'
  56. const schemaType = 'ProfilePage'
  57. customHTML = await TagsHtml.addTags(customHTML, {
  58. url,
  59. escapedTitle: escapeHTML(title),
  60. escapedSiteName: escapeHTML(siteName),
  61. escapedTruncatedDescription,
  62. image,
  63. ogType,
  64. twitterCard,
  65. schemaType,
  66. jsonldProfile: {
  67. createdAt: entity.createdAt,
  68. updatedAt: entity.updatedAt
  69. },
  70. indexationPolicy: entity.Actor.isOwned()
  71. ? 'always'
  72. : 'never'
  73. }, {})
  74. return customHTML
  75. }
  76. }