unoffical wafrn mirror
wafrn.net
atproto
social-network
activitypub
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}