account.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. import {
  2. AllowNull,
  3. BeforeDestroy,
  4. BelongsTo,
  5. Column,
  6. CreatedAt, DataType,
  7. Default,
  8. DefaultScope,
  9. ForeignKey,
  10. HasMany,
  11. Is,
  12. Model,
  13. Scopes,
  14. Table,
  15. UpdatedAt
  16. } from 'sequelize-typescript'
  17. import { Account, AccountSummary } from '../../../shared/models/actors'
  18. import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
  19. import { sendDeleteActor } from '../../lib/activitypub/send'
  20. import { ActorModel } from '../activitypub/actor'
  21. import { ApplicationModel } from '../application/application'
  22. import { ServerModel } from '../server/server'
  23. import { getSort, throwIfNotValid } from '../utils'
  24. import { VideoChannelModel } from '../video/video-channel'
  25. import { VideoCommentModel } from '../video/video-comment'
  26. import { UserModel } from './user'
  27. import { AvatarModel } from '../avatar/avatar'
  28. import { VideoPlaylistModel } from '../video/video-playlist'
  29. import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
  30. import { Op, Transaction, WhereOptions } from 'sequelize'
  31. export enum ScopeNames {
  32. SUMMARY = 'SUMMARY'
  33. }
  34. @DefaultScope(() => ({
  35. include: [
  36. {
  37. model: ActorModel, // Default scope includes avatar and server
  38. required: true
  39. }
  40. ]
  41. }))
  42. @Scopes(() => ({
  43. [ ScopeNames.SUMMARY ]: (whereActor?: WhereOptions) => {
  44. return {
  45. attributes: [ 'id', 'name' ],
  46. include: [
  47. {
  48. attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
  49. model: ActorModel.unscoped(),
  50. required: true,
  51. where: whereActor,
  52. include: [
  53. {
  54. attributes: [ 'host' ],
  55. model: ServerModel.unscoped(),
  56. required: false
  57. },
  58. {
  59. model: AvatarModel.unscoped(),
  60. required: false
  61. }
  62. ]
  63. }
  64. ]
  65. }
  66. }
  67. }))
  68. @Table({
  69. tableName: 'account',
  70. indexes: [
  71. {
  72. fields: [ 'actorId' ],
  73. unique: true
  74. },
  75. {
  76. fields: [ 'applicationId' ]
  77. },
  78. {
  79. fields: [ 'userId' ]
  80. }
  81. ]
  82. })
  83. export class AccountModel extends Model<AccountModel> {
  84. @AllowNull(false)
  85. @Column
  86. name: string
  87. @AllowNull(true)
  88. @Default(null)
  89. @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description', true))
  90. @Column(DataType.STRING(CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max))
  91. description: string
  92. @CreatedAt
  93. createdAt: Date
  94. @UpdatedAt
  95. updatedAt: Date
  96. @ForeignKey(() => ActorModel)
  97. @Column
  98. actorId: number
  99. @BelongsTo(() => ActorModel, {
  100. foreignKey: {
  101. allowNull: false
  102. },
  103. onDelete: 'cascade'
  104. })
  105. Actor: ActorModel
  106. @ForeignKey(() => UserModel)
  107. @Column
  108. userId: number
  109. @BelongsTo(() => UserModel, {
  110. foreignKey: {
  111. allowNull: true
  112. },
  113. onDelete: 'cascade'
  114. })
  115. User: UserModel
  116. @ForeignKey(() => ApplicationModel)
  117. @Column
  118. applicationId: number
  119. @BelongsTo(() => ApplicationModel, {
  120. foreignKey: {
  121. allowNull: true
  122. },
  123. onDelete: 'cascade'
  124. })
  125. Application: ApplicationModel
  126. @HasMany(() => VideoChannelModel, {
  127. foreignKey: {
  128. allowNull: false
  129. },
  130. onDelete: 'cascade',
  131. hooks: true
  132. })
  133. VideoChannels: VideoChannelModel[]
  134. @HasMany(() => VideoPlaylistModel, {
  135. foreignKey: {
  136. allowNull: false
  137. },
  138. onDelete: 'cascade',
  139. hooks: true
  140. })
  141. VideoPlaylists: VideoPlaylistModel[]
  142. @HasMany(() => VideoCommentModel, {
  143. foreignKey: {
  144. allowNull: false
  145. },
  146. onDelete: 'cascade',
  147. hooks: true
  148. })
  149. VideoComments: VideoCommentModel[]
  150. @BeforeDestroy
  151. static async sendDeleteIfOwned (instance: AccountModel, options) {
  152. if (!instance.Actor) {
  153. instance.Actor = await instance.$get('Actor', { transaction: options.transaction }) as ActorModel
  154. }
  155. if (instance.isOwned()) {
  156. return sendDeleteActor(instance.Actor, options.transaction)
  157. }
  158. return undefined
  159. }
  160. static load (id: number, transaction?: Transaction) {
  161. return AccountModel.findByPk(id, { transaction })
  162. }
  163. static loadByNameWithHost (nameWithHost: string) {
  164. const [ accountName, host ] = nameWithHost.split('@')
  165. if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName)
  166. return AccountModel.loadByNameAndHost(accountName, host)
  167. }
  168. static loadLocalByName (name: string) {
  169. const query = {
  170. where: {
  171. [ Op.or ]: [
  172. {
  173. userId: {
  174. [ Op.ne ]: null
  175. }
  176. },
  177. {
  178. applicationId: {
  179. [ Op.ne ]: null
  180. }
  181. }
  182. ]
  183. },
  184. include: [
  185. {
  186. model: ActorModel,
  187. required: true,
  188. where: {
  189. preferredUsername: name
  190. }
  191. }
  192. ]
  193. }
  194. return AccountModel.findOne(query)
  195. }
  196. static loadByNameAndHost (name: string, host: string) {
  197. const query = {
  198. include: [
  199. {
  200. model: ActorModel,
  201. required: true,
  202. where: {
  203. preferredUsername: name
  204. },
  205. include: [
  206. {
  207. model: ServerModel,
  208. required: true,
  209. where: {
  210. host
  211. }
  212. }
  213. ]
  214. }
  215. ]
  216. }
  217. return AccountModel.findOne(query)
  218. }
  219. static loadByUrl (url: string, transaction?: Transaction) {
  220. const query = {
  221. include: [
  222. {
  223. model: ActorModel,
  224. required: true,
  225. where: {
  226. url
  227. }
  228. }
  229. ],
  230. transaction
  231. }
  232. return AccountModel.findOne(query)
  233. }
  234. static listForApi (start: number, count: number, sort: string) {
  235. const query = {
  236. offset: start,
  237. limit: count,
  238. order: getSort(sort)
  239. }
  240. return AccountModel.findAndCountAll(query)
  241. .then(({ rows, count }) => {
  242. return {
  243. data: rows,
  244. total: count
  245. }
  246. })
  247. }
  248. static listLocalsForSitemap (sort: string) {
  249. const query = {
  250. attributes: [ ],
  251. offset: 0,
  252. order: getSort(sort),
  253. include: [
  254. {
  255. attributes: [ 'preferredUsername', 'serverId' ],
  256. model: ActorModel.unscoped(),
  257. where: {
  258. serverId: null
  259. }
  260. }
  261. ]
  262. }
  263. return AccountModel
  264. .unscoped()
  265. .findAll(query)
  266. }
  267. toFormattedJSON (): Account {
  268. const actor = this.Actor.toFormattedJSON()
  269. const account = {
  270. id: this.id,
  271. displayName: this.getDisplayName(),
  272. description: this.description,
  273. createdAt: this.createdAt,
  274. updatedAt: this.updatedAt,
  275. userId: this.userId ? this.userId : undefined
  276. }
  277. return Object.assign(actor, account)
  278. }
  279. toFormattedSummaryJSON (): AccountSummary {
  280. const actor = this.Actor.toFormattedJSON()
  281. return {
  282. id: this.id,
  283. name: actor.name,
  284. displayName: this.getDisplayName(),
  285. url: actor.url,
  286. host: actor.host,
  287. avatar: actor.avatar
  288. }
  289. }
  290. toActivityPubObject () {
  291. const obj = this.Actor.toActivityPubObject(this.name, 'Account')
  292. return Object.assign(obj, {
  293. summary: this.description
  294. })
  295. }
  296. isOwned () {
  297. return this.Actor.isOwned()
  298. }
  299. isOutdated () {
  300. return this.Actor.isOutdated()
  301. }
  302. getDisplayName () {
  303. return this.name
  304. }
  305. }