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