unoffical wafrn mirror wafrn.net
atproto social-network activitypub
at testPDSNotExplode 655 lines 15 kB view raw
1import { Model, Table, Column, DataType, ForeignKey, HasMany, BelongsToMany, BelongsTo } from 'sequelize-typescript' 2import { MfaDetails } from './mfaDetails.js' 3import { Notification } from './notification.js' 4import { Ask } from './ask.js' 5import { QuestionPollAnswer } from './questionPollAnswer.js' 6import { EmojiReaction } from './emojiReaction.js' 7import { UserOptions } from './userOptions.js' 8import { PushNotificationToken } from './pushNotificationToken.js' 9import { Emoji } from './emoji.js' 10import { UserEmojiRelation } from './userEmojiRelation.js' 11import { Follows } from './follows.js' 12import { Blocks } from './blocks.js' 13import { Mutes } from './mutes.js' 14import { ServerBlock } from './serverBlock.js' 15import { PostReport } from './postReport.js' 16import { SilencedPost } from './silencedPost.js' 17import { FederatedHost } from './federatedHost.js' 18import { Post } from './post.js' 19import { Media } from './media.js' 20import { PostMentionsUserRelation } from './postMentionsUserRelation.js' 21import { UserLikesPostRelations } from './userLikesPostRelations.js' 22import { UserBookmarkedPosts } from './userBookmarkedPosts.js' 23import { RemoteUserPostView } from './remoteUserPostView.js' 24import { 25 BelongsToGetAssociationMixin, 26 BelongsToManyAddAssociationMixin, 27 BelongsToManyGetAssociationsMixin, 28 BelongsToManyRemoveAssociationMixin, 29 BelongsToManyRemoveAssociationsMixin, 30 BelongsToManySetAssociationsMixin, 31 HasManyGetAssociationsMixin, 32 HasManyRemoveAssociationMixin 33} from 'sequelize' 34import { Col } from 'sequelize/lib/utils' 35import { UserFollowHashtags } from './userFollowHashtag.js' 36import { completeEnvironment } from '../utils/backendOptions.js' 37 38export interface UserAttributes { 39 id?: string 40 createdAt?: Date 41 updatedAt?: Date 42 email?: string | null 43 description?: string 44 descriptionMarkdown?: string 45 name?: string 46 url: string 47 NSFW?: boolean 48 avatar?: string 49 password?: string 50 birthDate?: Date 51 activated?: boolean | null 52 requestedPasswordReset?: Date | null 53 activationCode?: string 54 registerIp?: string 55 lastLoginIp?: string 56 lastTimeNotificationsCheck?: Date 57 privateKey?: string | null 58 publicKey?: string 59 federatedHostId?: string | null 60 remoteInbox?: string 61 remoteId?: string 62 remoteMentionUrl?: string 63 isBot?: boolean 64 banned?: boolean | null 65 role?: number 66 manuallyAcceptsFollows?: boolean 67 headerImage?: string 68 followersCollectionUrl?: string 69 followingCollectionUrl?: string 70 followerCount?: number 71 followingCount?: number 72 disableEmailNotifications?: boolean 73 enableBsky?: boolean 74 bskyAuthData?: string | null 75 bskyAppPassword?: string | null 76 bskyDid?: string | null 77 lastActiveAt?: Date 78 hideFollows?: Boolean 79 hideProfileNotLoggedIn?: Boolean 80 emailVerified: Boolean | null 81 selfDeleted: Boolean | null 82 userMigratedTo: String | null 83} 84 85@Table({ 86 tableName: 'users', 87 modelName: 'users', 88 timestamps: true, 89 scopes: { 90 full: { 91 //attributes: {} 92 } 93 }, 94 defaultScope: { 95 attributes: { 96 exclude: [ 97 'password', 98 'email', 99 'privateKey', 100 'lastLoginIp', 101 'registerIp', 102 'bskyAuthData', 103 'bskyAppPassword', 104 'birthDate' 105 ] 106 } 107 } 108}) 109export class User extends Model<UserAttributes, UserAttributes> implements UserAttributes { 110 @Column({ 111 primaryKey: true, 112 type: DataType.UUID, 113 defaultValue: DataType.UUIDV4 114 }) 115 declare id: string 116 117 @Column({ 118 allowNull: true, 119 type: DataType.STRING(768) 120 }) 121 declare email: string | null 122 123 @Column({ 124 allowNull: true, 125 type: DataType.STRING 126 }) 127 declare description: string 128 129 @Column({ 130 allowNull: true, 131 type: DataType.STRING 132 }) 133 declare descriptionMarkdown: string 134 135 @Column({ 136 allowNull: true, 137 type: DataType.STRING 138 }) 139 declare name: string 140 141 @Column({ 142 type: DataType.STRING(768) 143 }) 144 declare url: string 145 146 @Column({ 147 allowNull: true, 148 type: DataType.BOOLEAN, 149 defaultValue: false 150 }) 151 declare NSFW: boolean 152 153 @Column({ 154 allowNull: true, 155 type: DataType.STRING 156 }) 157 declare avatar: string 158 159 @Column({ 160 allowNull: true, 161 type: DataType.STRING 162 }) 163 declare password: string 164 165 @Column({ 166 allowNull: true, 167 type: DataType.DATE 168 }) 169 declare birthDate: Date 170 171 @Column({ 172 allowNull: true, 173 type: DataType.BOOLEAN 174 }) 175 declare activated: boolean | null 176 177 @Column({ 178 allowNull: true, 179 type: DataType.BOOLEAN 180 }) 181 declare emailVerified: boolean | null 182 183 @Column({ 184 allowNull: true, 185 type: DataType.DATE 186 }) 187 declare requestedPasswordReset: Date | null 188 189 @Column({ 190 allowNull: true, 191 type: DataType.STRING(255) 192 }) 193 declare activationCode: string 194 195 @Column({ 196 allowNull: true, 197 type: DataType.STRING(255) 198 }) 199 declare registerIp: string 200 201 @Column({ 202 allowNull: true, 203 type: DataType.STRING(255) 204 }) 205 declare lastLoginIp: string 206 207 @Column({ 208 type: DataType.DATE, 209 defaultValue: new Date(0) 210 }) 211 declare lastTimeNotificationsCheck: Date 212 213 @Column({ 214 allowNull: true, 215 type: DataType.STRING 216 }) 217 declare privateKey: string | null 218 219 @Column({ 220 allowNull: true, 221 type: DataType.STRING 222 }) 223 declare publicKey: string 224 225 @ForeignKey(() => FederatedHost) 226 @Column({ 227 allowNull: true, 228 type: DataType.UUID 229 }) 230 declare federatedHostId: string | null 231 232 @Column({ 233 allowNull: true, 234 type: DataType.STRING 235 }) 236 declare remoteInbox: string 237 238 @Column({ 239 allowNull: true, 240 type: DataType.STRING(768) 241 }) 242 declare remoteId: string 243 244 @Column({ 245 allowNull: true, 246 type: DataType.STRING 247 }) 248 declare remoteMentionUrl: string 249 250 @Column({ 251 allowNull: true, 252 type: DataType.BOOLEAN, 253 defaultValue: false 254 }) 255 declare isBot: boolean 256 257 @Column({ 258 allowNull: true, 259 type: DataType.BOOLEAN, 260 defaultValue: false 261 }) 262 declare banned: boolean | null 263 264 @Column({ 265 allowNull: true, 266 type: DataType.INTEGER, 267 defaultValue: 0 268 }) 269 declare role: number 270 271 @Column({ 272 allowNull: true, 273 type: DataType.BOOLEAN, 274 defaultValue: false 275 }) 276 declare manuallyAcceptsFollows: boolean 277 278 @Column({ 279 allowNull: true, 280 type: DataType.STRING 281 }) 282 declare headerImage: string 283 284 @Column({ 285 allowNull: true, 286 type: DataType.STRING 287 }) 288 declare followersCollectionUrl: string 289 290 @Column({ 291 allowNull: true, 292 type: DataType.STRING 293 }) 294 declare followingCollectionUrl: string 295 296 @Column({ 297 allowNull: true, 298 type: DataType.INTEGER, 299 defaultValue: 0 300 }) 301 declare followerCount: number 302 303 @Column({ 304 allowNull: true, 305 type: DataType.INTEGER, 306 defaultValue: 0 307 }) 308 declare followingCount: number 309 310 @Column({ 311 allowNull: true, 312 type: DataType.BOOLEAN, 313 defaultValue: false 314 }) 315 declare disableEmailNotifications: boolean 316 317 @Column({ 318 allowNull: true, 319 type: DataType.BOOLEAN, 320 defaultValue: false 321 }) 322 declare enableBsky: boolean 323 324 @Column({ 325 allowNull: true, 326 type: DataType.STRING 327 }) 328 declare bskyAuthData: string | null 329 330 @Column({ 331 allowNull: true, 332 type: DataType.STRING 333 }) 334 declare bskyAppPassword: string | null 335 336 @Column({ 337 allowNull: true, 338 type: DataType.STRING(768) 339 }) 340 declare bskyDid: string | null 341 342 @Column({ 343 allowNull: true, 344 type: DataType.DATE, 345 defaultValue: new Date(0) 346 }) 347 declare lastActiveAt: Date 348 349 @Column({ 350 allowNull: true, 351 type: DataType.BOOLEAN, 352 defaultValue: false 353 }) 354 declare hideFollows: Boolean 355 356 @Column({ 357 allowNull: true, 358 type: DataType.BOOLEAN, 359 defaultValue: false 360 }) 361 declare hideProfileNotLoggedIn: Boolean 362 363 @HasMany(() => MfaDetails, { 364 sourceKey: 'id' 365 }) 366 declare mfaDetails: MfaDetails[] 367 368 @HasMany(() => Notification, { 369 foreignKey: 'notifiedUserId' 370 }) 371 declare incomingNotifications: Notification[] 372 373 @HasMany(() => Notification, { 374 foreignKey: 'userId' 375 }) 376 declare outgoingNotifications: Notification[] 377 378 @HasMany(() => Ask, { 379 foreignKey: 'userAsker' 380 }) 381 declare userAsker: Ask[] 382 383 @HasMany(() => Ask, { 384 foreignKey: 'userAsked' 385 }) 386 declare userAsked: Ask[] 387 388 @HasMany(() => QuestionPollAnswer, { 389 sourceKey: 'id' 390 }) 391 declare questionPollAnswers: QuestionPollAnswer[] 392 393 @HasMany(() => EmojiReaction, { 394 sourceKey: 'id' 395 }) 396 declare emojiReacions: EmojiReaction[] 397 398 @HasMany(() => UserOptions, { 399 sourceKey: 'id' 400 }) 401 declare userOptions: UserOptions[] 402 403 @HasMany(() => PushNotificationToken, { 404 sourceKey: 'id' 405 }) 406 declare pushNotificationTokens: PushNotificationToken[] 407 408 @BelongsToMany(() => Emoji, () => UserEmojiRelation) 409 declare emojis: Emoji[] 410 declare setEmojis: BelongsToManySetAssociationsMixin<Emoji, string> 411 declare removeEmojis: BelongsToManyRemoveAssociationsMixin<Emoji, string> 412 413 @HasMany(() => Follows, { 414 foreignKey: 'followerId' 415 }) 416 declare followerFollows: Follows[] 417 418 @HasMany(() => Follows, { 419 foreignKey: 'followedId' 420 }) 421 declare followedFollows: Follows[] 422 423 @BelongsToMany(() => User, () => Follows, 'followedId', 'followerId') 424 declare follower: User[] 425 declare getFollower: BelongsToManyGetAssociationsMixin<User> 426 declare removeFollower: BelongsToManyRemoveAssociationMixin<User, string> 427 428 @BelongsToMany(() => User, () => Follows, 'followerId', 'followedId') 429 declare followed: User[] 430 declare getFollowed: BelongsToManyGetAssociationsMixin<User> 431 declare removeFollowed: BelongsToManyRemoveAssociationMixin<User, string> 432 433 @HasMany(() => Blocks, { 434 foreignKey: 'blockerId' 435 }) 436 declare blockerBlocks: Blocks[] 437 438 @HasMany(() => Blocks, { 439 foreignKey: 'blockedId' 440 }) 441 declare blockedBlocks: Blocks[] 442 443 @BelongsToMany(() => User, () => Blocks, 'blockedId', 'blockerId') 444 declare blocker: User[] 445 declare addBlocker: BelongsToManyAddAssociationMixin<User, string> 446 declare removeBlocker: BelongsToManyRemoveAssociationMixin<User, string> 447 448 @BelongsToMany(() => User, () => Blocks, 'blockerId', 'blockedId') 449 declare blocked: User[] 450 451 @HasMany(() => Mutes, { 452 foreignKey: 'muterId' 453 }) 454 declare muterMutes: Mutes[] 455 456 @HasMany(() => Mutes, { 457 foreignKey: 'mutedId' 458 }) 459 declare mutedMutes: Mutes[] 460 461 @BelongsToMany(() => User, () => Mutes, 'mutedId', 'muterId') 462 declare muter: User[] 463 declare addMuter: BelongsToManyAddAssociationMixin<User, string> 464 declare removeMuter: BelongsToManyRemoveAssociationMixin<User, string> 465 466 @BelongsToMany(() => User, () => Mutes, 'muterId', 'mutedId') 467 declare muted: User[] 468 469 @HasMany(() => ServerBlock, { 470 sourceKey: 'id' 471 }) 472 declare serverBlocks: ServerBlock[] 473 474 @HasMany(() => PostReport, { 475 sourceKey: 'id' 476 }) 477 declare postReports: PostReport[] 478 479 @HasMany(() => SilencedPost, { 480 sourceKey: 'id' 481 }) 482 declare silencedPosts: SilencedPost[] 483 484 @BelongsTo(() => FederatedHost, { 485 foreignKey: { 486 name: 'federatedHostId', 487 allowNull: true 488 } 489 }) 490 declare federatedHost: FederatedHost 491 declare getFederatedHost: BelongsToGetAssociationMixin<FederatedHost> 492 493 @HasMany(() => Post, { 494 sourceKey: 'id' 495 }) 496 declare posts: Post[] 497 declare getPosts: HasManyGetAssociationsMixin<Post> 498 499 @HasMany(() => Media, { 500 sourceKey: 'id' 501 }) 502 declare medias: Media[] 503 504 @HasMany(() => PostMentionsUserRelation, { 505 sourceKey: 'id' 506 }) 507 declare pMURs: PostMentionsUserRelation[] 508 509 @BelongsToMany(() => Post, () => PostMentionsUserRelation) 510 declare mentionPost: Post[] 511 512 @HasMany(() => UserLikesPostRelations, { 513 sourceKey: 'id' 514 }) 515 declare userLikesPostRelations: UserLikesPostRelations[] 516 declare getUserLikesPostRelations: HasManyGetAssociationsMixin<UserLikesPostRelations> 517 518 @HasMany(() => UserBookmarkedPosts, { 519 sourceKey: 'id' 520 }) 521 declare userBookmarkedPosts: UserBookmarkedPosts[] 522 declare getUserBookmarkedPosts: HasManyGetAssociationsMixin<UserBookmarkedPosts> 523 524 @HasMany(() => RemoteUserPostView, { 525 sourceKey: 'id' 526 }) 527 declare remoteUserPostViewList: RemoteUserPostView[] 528 529 @HasMany(() => UserFollowHashtags, { 530 sourceKey: 'id' 531 }) 532 declare userFollowedHashtagList: UserFollowHashtags[] 533 534 @Column({ 535 allowNull: true, 536 type: DataType.BOOLEAN, 537 defaultValue: false 538 }) 539 declare selfDeleted: boolean 540 541 @Column({ 542 allowNull: true, 543 type: DataType.STRING(768) 544 }) 545 declare userMigratedTo: string 546 547 get isRemoteUser() { 548 return !!this.url.startsWith('@') 549 } 550 551 get isLocalUser() { 552 return !this.isRemoteUser 553 } 554 555 get isBlueskyUser() { 556 return !!(this.url.split('@').length == 2 && this.bskyDid) 557 } 558 559 get isFediverseUser() { 560 return !!(this.url.split('@').length == 3 && this.remoteId) 561 } 562 563 // the username part of the handle, without the domain for both bsky and fedi 564 get shortHandle() { 565 if (this.isBlueskyUser) 566 return this.url.split('@')[1].split('.')[0]; 567 568 if (this.isFediverseUser) 569 return this.url.split('@')[1] 570 571 return this.url 572 } 573 574 // the username part of the handle. For bluesky also includes the domain, but for fedi it doesn't 575 get longHandle() { 576 if (this.isBlueskyUser || this.isFediverseUser) 577 return this.url.split('@')[1] 578 579 return this.url 580 } 581 582 // the full handle regardless if it's a local user or not. Fedi format for local users and fedi users; Bsky format for bsky users 583 get fullHandle() { 584 return this.isRemoteUser ? this.url : `@${this.url}@${completeEnvironment.instanceUrl}` 585 } 586 587 get fullUrl() { 588 return this.remoteId || `${completeEnvironment.frontendUrl}/blog/${this.url}` 589 } 590 591 get fullFediverseUrl() { 592 return this.isRemoteUser ? this.remoteId : `${completeEnvironment.frontendUrl}/fediverse/blog/${this.url}` 593 } 594 595 get avatarFullUrl() { 596 return this.isRemoteUser ? this.avatar : `${completeEnvironment.mediaUrl}${this.avatar}` 597 } 598 599 get headerImageFullUrl() { 600 return this.isRemoteUser ? this.headerImage : `${completeEnvironment.mediaUrl}${this.headerImage}` 601 } 602} 603 604export function getLocalUsernameFromLocalRemoteId(remoteId: string) { 605 return remoteId.split(`${completeEnvironment.instanceUrl}/fediverse/blog/`)[1].split('@')[0] 606} 607 608export function isLocalRemoteId(remoteId: string) { 609 return remoteId.startsWith(completeEnvironment.frontendUrl) 610} 611 612export interface HandleData { 613 username: string 614 handle: string 615 domain: string 616 type: "fediverse" | "bluesky" | "local" 617} 618 619export function splitHandle(handleString: string): HandleData { 620 handleString = handleString.trim() 621 if (handleString.startsWith('@') && handleString.length > 3) { 622 const userData = handleString.split('@') 623 if (userData.length === 3 && userData[0] == '') { 624 const username = userData[1] 625 const domain = userData[2] 626 return { 627 handle: handleString, 628 username: username, 629 domain: domain, 630 type: "fediverse" 631 } 632 } else if (userData.length === 2 && userData[0] == '') { 633 const handle = userData[1] 634 const elements = handle.split('.') 635 const username = elements.shift() as string 636 const domain = elements.join('.') 637 return { 638 handle: handle, 639 username: username, 640 domain: domain, 641 type: "bluesky" 642 } 643 } 644 } 645 return { 646 username: handleString, 647 handle: handleString, 648 domain: completeEnvironment.instanceUrl, 649 type: "local" 650 } 651} 652 653export function addHandlePrefix(handle: string) { 654 return handle.startsWith('@') ? handle : `@${handle}` 655}